Factor out massive code duplication in route creation stuff.
authorDavid Robillard <d@drobilla.net>
Tue, 23 Feb 2010 22:45:07 +0000 (22:45 +0000)
committerDavid Robillard <d@drobilla.net>
Tue, 23 Feb 2010 22:45:07 +0000 (22:45 +0000)
Fix auto-connect logic for multiply-typed routes.

git-svn-id: svn://localhost/ardour2/branches/3.0@6711 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/chan_count.h
libs/ardour/ardour/session.h
libs/ardour/session.cc

index 3dc6423607ce625a4e07fa7c7831e1f6e5eb0b5e..b24ff2336c42a44c4c4b09ba1e4d5c9aedd046f4 100644 (file)
@@ -114,6 +114,13 @@ public:
                return ret;
        }
 
+       ChanCount& operator+=(const ChanCount& other) {
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       _counts[*t] += other._counts[*t];
+               }
+               return *this;
+       }
+
        static ChanCount min(const ChanCount& a, const ChanCount& b) {
                ChanCount ret;
                for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
index 02c5cc2391573751cc69c18c7ab13000c11df49b..edeeeb11efe58d9644e48fd7c5494f1f5efb1fd5 100644 (file)
@@ -1235,6 +1235,11 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
 
        void route_processors_changed (RouteProcessorChange);
 
+       bool find_route_name (const char* base, uint32_t& id, char* name, size_t name_len);
+       void count_existing_route_channels (ChanCount& in, ChanCount& out);
+       void auto_connect_route (boost::shared_ptr<Route> route,
+               ChanCount& existing_inputs, ChanCount& existing_outputs);
+
        /* mixer stuff */
 
        bool solo_update_disabled;
index c1fe4bb8c09d0be1e6670ebb23cf3303b20afc05..9797ecd5ab554efb64cccfe762a9514fab467223 100644 (file)
@@ -1524,60 +1524,67 @@ Session::resort_routes_using (shared_ptr<RouteList> r)
 
 }
 
+/** Find the route name starting with \a base with the lowest \a id.
+ *
+ * Names are constructed like e.g. "Audio 3" for base="Audio" and id=3.
+ * The available route name with the lowest ID will be used, and \a id
+ * will be set to the ID.
+ *
+ * \return false if a route name could not be found, and \a track_name
+ * and \a id do not reflect a free route name.
+ */
+bool
+Session::find_route_name (const char* base, uint32_t& id, char* name, size_t name_len)
+{
+       do {
+               snprintf (name, name_len, "%s %" PRIu32, base, id);
+
+               if (route_by_name (name) == 0) {
+                       return true;
+               }
+
+               ++id;
+
+       } while (id < (UINT_MAX-1));
+
+       return false;
+}
+
+void
+Session::count_existing_route_channels (ChanCount& in, ChanCount& out)
+{
+       in  = ChanCount::ZERO;
+       out = ChanCount::ZERO;
+       shared_ptr<RouteList> r = routes.reader ();
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               if (!(*i)->is_hidden()) {
+                       in += (*i)->n_inputs();
+                       out     += (*i)->n_outputs();
+               }
+       }
+}
+
 list<boost::shared_ptr<MidiTrack> >
 Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many)
 {
        char track_name[32];
-       uint32_t track_id = 0;
-       uint32_t n = 0;
-       uint32_t channels_used = 0;
+       uint32_t track_id = 1;
+       ChanCount existing_inputs;
+       ChanCount existing_outputs;
        string port;
        RouteList new_routes;
        list<boost::shared_ptr<MidiTrack> > ret;
        uint32_t control_id;
 
-       /* count existing midi tracks */
-
-       {
-               shared_ptr<RouteList> r = routes.reader ();
-
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if (boost::dynamic_pointer_cast<MidiTrack>(*i) != 0) {
-                               if (!(*i)->is_hidden()) {
-                                       n++;
-                                       channels_used += (*i)->n_inputs().n_midi();
-                               }
-                       }
-               }
-       }
-
-       vector<string> physinputs;
-       vector<string> physoutputs;
-
-       _engine.get_physical_outputs (DataType::MIDI, physoutputs);
-       _engine.get_physical_inputs (DataType::MIDI, physinputs);
+       count_existing_route_channels (existing_inputs, existing_outputs);
 
        control_id = ntracks() + nbusses();
 
        while (how_many) {
-
-               /* check for duplicate route names, since we might have pre-existing
-                  routes with this name (e.g. create Audio1, Audio2, delete Audio1,
-                  save, close,restart,add new route - first named route is now
-                  Audio2)
-               */
-
-
-               do {
-                       ++track_id;
-
-                       snprintf (track_name, sizeof(track_name), "Midi %" PRIu32, track_id);
-
-                       if (route_by_name (track_name) == 0) {
-                               break;
-                       }
-
-               } while (track_id < (UINT_MAX-1));
+               if (!find_route_name ("Midi", track_id, track_name, sizeof(track_name))) {
+                       error << "cannot find name for new midi track" << endmsg;
+                       goto failed;
+               }
 
                shared_ptr<MidiTrack> track;
 
@@ -1597,83 +1604,7 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
                                goto failed;
                        }
 
-                       if (!physinputs.empty()) {
-                               uint32_t nphysical_in = physinputs.size();
-
-                               for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) {
-
-                                       port = "";
-
-                                       if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                               port = physinputs[(channels_used+x)%nphysical_in];
-                                       }
-
-                                       if (port.length() && track->input()->connect (track->input()->nth(x), port, this)) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (!physoutputs.empty()) {
-                               uint32_t nphysical_out = physoutputs.size();
-
-                               for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
-                                       port = "";
-
-                                       if (Config->get_output_auto_connect() & AutoConnectPhysical) {
-                                               port = physoutputs[(channels_used+x)%nphysical_out];
-                                       } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                               if (_master_out && _master_out->n_inputs().n_midi() > 0) {
-                                                       port = _master_out->input()->nth (x % _master_out->input()->n_ports().n_midi())->name();
-                                               }
-                                       }
-
-                                       if (port.length() && track->output()->connect (track->output()->nth(x), port, this)) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       channels_used += track->n_inputs ().n_audio();
-
-
-
-                       /*
-                       if (nphysical_in) {
-                               for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) {
-
-                                       port = "";
-
-                                       if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                               port = physinputs[(channels_used+x)%nphysical_in];
-                                       }
-
-                                       if (port.length() && track->connect_input (track->input (x), port, this)) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
-
-                               port = "";
-
-                               if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
-                                       port = physoutputs[(channels_used+x)%nphysical_out];
-                               } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                       if (_master_out) {
-                                               port = _master_out->input (x%_master_out->n_inputs().n_midi())->name();
-                                       }
-                               }
-
-                               if (port.length() && track->connect_output (track->output (x), port, this)) {
-                                       break;
-                               }
-                       }
-
-                       channels_used += track->n_inputs ().n_midi();
-
-                       */
+                       auto_connect_route (track, existing_inputs, existing_outputs);
 
                        track->midi_diskstream()->non_realtime_input_change();
                        if (route_group) {
@@ -1681,7 +1612,7 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
                        }
 
                        track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-                       //track->set_remote_control_id (control_id);
+                       track->set_remote_control_id (control_id);
 
                        new_routes.push_back (track);
                        ret.push_back (track);
@@ -1734,60 +1665,102 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
        return ret;
 }
 
-list<boost::shared_ptr<AudioTrack> >
-Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,  uint32_t how_many)
-{
-       char track_name[32];
-       uint32_t track_id = 0;
-       uint32_t n = 0;
-       uint32_t channels_used = 0;
-       string port;
-       RouteList new_routes;
-       list<boost::shared_ptr<AudioTrack> > ret;
-       uint32_t control_id;
+void
+Session::auto_connect_route (boost::shared_ptr<Route> route,
+               ChanCount& existing_inputs, ChanCount& existing_outputs)
+{
+       /* If both inputs and outputs are auto-connected to physical ports,
+          use the max of input and output offsets to ensure auto-connected
+          port numbers always match up (e.g. the first audio input and the
+          first audio output of the route will have the same physical
+          port number).  Otherwise just use the lowest input or output
+          offset possible.
+       */
+       const bool in_out_physical =
+                  (Config->get_input_auto_connect() & AutoConnectPhysical)
+               && (Config->get_output_auto_connect() & AutoConnectPhysical);
 
-       /* count existing audio tracks */
+       const ChanCount in_offset = in_out_physical
+               ? ChanCount::max(existing_inputs, existing_outputs)
+               : existing_inputs;
 
-       {
-               shared_ptr<RouteList> r = routes.reader ();
+       const ChanCount out_offset = in_out_physical
+               ? ChanCount::max(existing_inputs, existing_outputs)
+               : existing_outputs;
 
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if (boost::dynamic_pointer_cast<AudioTrack>(*i) != 0) {
-                               if (!(*i)->is_hidden()) {
-                                       n++;
-                                       channels_used += (*i)->n_inputs().n_audio();
+       static string empty_string;
+       string& port = empty_string;
+
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               vector<string> physinputs;
+               vector<string> physoutputs;
+
+               _engine.get_physical_outputs (*t, physoutputs);
+               _engine.get_physical_inputs (*t, physinputs);
+
+               if (!physinputs.empty()) {
+                       uint32_t nphysical_in = physinputs.size();
+                       for (uint32_t i = 0; i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
+                               port = empty_string;
+
+                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+                                       port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
+                               }
+
+                               if (!port.empty() && route->input()->connect (
+                                               route->input()->ports().port(*t, i), port, this)) {
+                                       break;
                                }
                        }
                }
-       }
-
-       vector<string> physinputs;
-       vector<string> physoutputs;
 
-       _engine.get_physical_outputs (DataType::AUDIO, physoutputs);
-       _engine.get_physical_inputs (DataType::AUDIO, physinputs);
+               if (!physoutputs.empty()) {
+                       uint32_t nphysical_out = physoutputs.size();
+                       for (uint32_t i = 0; i < route->n_outputs().get(*t); ++i) {
+                               port = empty_string;
 
-       control_id = ntracks() + nbusses() + 1;
-
-       while (how_many) {
+                               if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+                                       port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
+                               } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+                                       if (_master_out && _master_out->n_inputs().get(*t) > 0) {
+                                               port = _master_out->input()->ports().port(*t,
+                                                               i % _master_out->input()->n_ports().get(*t))->name();
+                                       }
+                               }
 
-               /* check for duplicate route names, since we might have pre-existing
-                  routes with this name (e.g. create Audio1, Audio2, delete Audio1,
-                  save, close,restart,add new route - first named route is now
-                  Audio2)
-               */
+                               if (!port.empty() && route->output()->connect (
+                                               route->output()->ports().port(*t, i), port, this)) {
+                                       break;
+                               }
+                       }
+               }
+       }
 
+       existing_inputs += route->n_inputs();
+       existing_outputs += route->n_outputs();
+}
 
-               do {
-                       ++track_id;
+list<boost::shared_ptr<AudioTrack> >
+Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,  uint32_t how_many)
+{
+       char track_name[32];
+       uint32_t track_id = 1;
+       ChanCount existing_inputs;
+       ChanCount existing_outputs;
+       string port;
+       RouteList new_routes;
+       list<boost::shared_ptr<AudioTrack> > ret;
+       uint32_t control_id;
 
-                       snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, track_id);
+       count_existing_route_channels (existing_inputs, existing_outputs);
 
-                       if (route_by_name (track_name) == 0) {
-                               break;
-                       }
+       control_id = ntracks() + nbusses() + 1;
 
-               } while (track_id < (UINT_MAX-1));
+       while (how_many) {
+               if (!find_route_name ("Audio", track_id, track_name, sizeof(track_name))) {
+                       error << "cannot find name for new audio track" << endmsg;
+                       goto failed;
+               }
 
                shared_ptr<AudioTrack> track;
 
@@ -1797,57 +1770,22 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                        track = boost::shared_ptr<AudioTrack>(at);
 
                        if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
-                               error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+                               error << string_compose (
+                                                       _("cannot configure %1 in/%2 out configuration for new audio track"),
                                                         input_channels, output_channels)
                                      << endmsg;
                                goto failed;
                        }
 
                        if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
-                               error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+                               error << string_compose (
+                                                       _("cannot configure %1 in/%2 out configuration for new audio track"),
                                                         input_channels, output_channels)
                                      << endmsg;
                                goto failed;
                        }
 
-                       if (!physinputs.empty()) {
-                               uint32_t nphysical_in = physinputs.size();
-
-                               for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) {
-
-                                       port = "";
-
-                                       if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                               port = physinputs[(channels_used+x)%nphysical_in];
-                                       }
-
-                                       if (port.length() && track->input()->connect (track->input()->nth(x), port, this)) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (!physoutputs.empty()) {
-                               uint32_t nphysical_out = physoutputs.size();
-
-                               for (uint32_t x = 0; x < track->n_outputs().n_audio(); ++x) {
-                                       port = "";
-
-                                       if (Config->get_output_auto_connect() & AutoConnectPhysical) {
-                                               port = physoutputs[(channels_used+x)%nphysical_out];
-                                       } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                               if (_master_out && _master_out->n_inputs().n_audio() > 0) {
-                                                       port = _master_out->input()->nth (x % _master_out->input()->n_ports().n_audio())->name();
-                                               }
-                                       }
-
-                                       if (port.length() && track->output()->connect (track->output()->nth(x), port, this)) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       channels_used += track->n_inputs ().n_audio();
+                       auto_connect_route (track, existing_inputs, existing_outputs);
 
                        if (route_group) {
                                route_group->add (track);
@@ -1942,52 +1880,21 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou
 {
        char bus_name[32];
        uint32_t bus_id = 1;
-       uint32_t n = 0;
-       uint32_t channels_used = 0;
+       ChanCount existing_inputs;
+       ChanCount existing_outputs;
        string port;
        RouteList ret;
        uint32_t control_id;
 
-       /* count existing audio busses */
-
-       {
-               shared_ptr<RouteList> r = routes.reader ();
-
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if (boost::dynamic_pointer_cast<Track>(*i) == 0) {
-                               /* its a bus ? */
-                               if (!(*i)->is_hidden() && (*i)->name() != _("master")) {
-                                       bus_id++;
-                                       n++;
-                                       channels_used += (*i)->n_inputs().n_audio();
-                               }
-                       }
-               }
-       }
-
-       vector<string> physinputs;
-       vector<string> physoutputs;
-
-       _engine.get_physical_outputs (DataType::AUDIO, physoutputs);
-       _engine.get_physical_inputs (DataType::AUDIO, physinputs);
-
-       n_physical_audio_outputs = physoutputs.size();
-       n_physical_audio_inputs = physinputs.size();
+       count_existing_route_channels (existing_inputs, existing_outputs);
 
        control_id = ntracks() + nbusses() + 1;
 
        while (how_many) {
-
-               do {
-                       snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id);
-
-                       bus_id++;
-
-                       if (route_by_name (bus_name) == 0) {
-                               break;
-                       }
-
-               } while (bus_id < (UINT_MAX-1));
+               if (!find_route_name ("Bus", bus_id, bus_name, sizeof(bus_name))) {
+                       error << "cannot find name for new audio bus" << endmsg;
+                       goto failure;
+               }
 
                try {
                        Route* rt = new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO);
@@ -2009,35 +1916,7 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou
                                goto failure;
                        }
 
-                       for (uint32_t x = 0; n_physical_audio_inputs && x < bus->input()->n_ports().n_audio(); ++x) {
-                               port = "";
-
-                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                       port = physinputs[((n+x)%n_physical_audio_inputs)];
-                               }
-
-                               if (port.length() && bus->input()->connect (bus->input()->nth (x), port, this)) {
-                                       break;
-                               }
-                       }
-
-                       for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs().n_audio(); ++x) {
-                               port = "";
-
-                               if (Config->get_output_auto_connect() & AutoConnectPhysical) {
-                                       port = physoutputs[((n+x)%n_physical_outputs)];
-                               } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                       if (_master_out) {
-                                               port = _master_out->input()->nth (x%_master_out->input()->n_ports().n_audio())->name();
-                                       }
-                               }
-
-                               if (port.length() && bus->output()->connect (bus->output()->nth(x), port, this)) {
-                                       break;
-                               }
-                       }
-
-                       channels_used += bus->n_inputs ().n_audio();
+                       auto_connect_route (bus, existing_inputs, existing_outputs);
 
                        if (route_group) {
                                route_group->add (bus);
@@ -2100,19 +1979,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
                std::string node_name = IO::name_from_state (*node_copy.children().front());
 
                /* generate a new name by adding a number to the end of the template name */
-
-               do {
-                       snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number);
-
-                       number++;
-
-                       if (route_by_name (name) == 0) {
-                               break;
-                       }
-
-               } while (number < UINT_MAX);
-
-               if (number == UINT_MAX) {
+               if (!find_route_name (node_name.c_str(), number, name, sizeof(name))) {
                        fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
                        /*NOTREACHED*/
                }