2 Copyright (C) 2002-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <boost/shared_ptr.hpp>
23 #include "ardour/audio_track.h"
24 #include "ardour/audioengine.h"
25 #include "ardour/bundle.h"
26 #include "ardour/user_bundle.h"
27 #include "ardour/io_processor.h"
28 #include "ardour/midi_track.h"
29 #include "ardour/port.h"
30 #include "ardour/session.h"
31 #include "ardour/auditioner.h"
33 #include "gui_thread.h"
34 #include "port_group.h"
35 #include "port_matrix.h"
36 #include "time_axis_view.h"
37 #include "public_editor.h"
43 using namespace ARDOUR
;
45 /** PortGroup constructor.
48 PortGroup::PortGroup (std::string
const & n
)
54 PortGroup::~PortGroup()
56 for (BundleList::iterator i
= _bundles
.begin(); i
!= _bundles
.end(); ++i
) {
62 /** Add a bundle to a group.
64 * @param allow_dups true to allow the group to contain more than one bundle with the same port, otherwise false.
67 PortGroup::add_bundle (boost::shared_ptr
<Bundle
> b
, bool allow_dups
)
69 add_bundle_internal (b
, boost::shared_ptr
<IO
> (), false, Gdk::Color (), allow_dups
);
72 /** Add a bundle to a group.
74 * @param io IO whose ports are in the bundle.
77 PortGroup::add_bundle (boost::shared_ptr
<Bundle
> b
, boost::shared_ptr
<IO
> io
)
79 add_bundle_internal (b
, io
, false, Gdk::Color (), false);
82 /** Add a bundle to a group.
84 * @param c Colour to represent the bundle with.
87 PortGroup::add_bundle (boost::shared_ptr
<Bundle
> b
, boost::shared_ptr
<IO
> io
, Gdk::Color c
)
89 add_bundle_internal (b
, io
, true, c
, false);
92 PortGroup::BundleRecord::BundleRecord (boost::shared_ptr
<ARDOUR::Bundle
> b
, boost::shared_ptr
<ARDOUR::IO
> iop
, Gdk::Color c
, bool has_c
)
101 PortGroup::add_bundle_internal (boost::shared_ptr
<Bundle
> b
, boost::shared_ptr
<IO
> io
, bool has_colour
, Gdk::Color colour
, bool allow_dups
)
107 /* don't add this bundle if we already have one with the same ports */
109 BundleList::iterator i
= _bundles
.begin ();
110 while (i
!= _bundles
.end() && b
->has_same_ports ((*i
)->bundle
) == false) {
114 if (i
!= _bundles
.end ()) {
119 BundleRecord
* br
= new BundleRecord (b
, io
, colour
, has_colour
);
120 b
->Changed
.connect (br
->changed_connection
, invalidator (*this), ui_bind (&PortGroup::bundle_changed
, this, _1
), gui_context());
121 _bundles
.push_back (br
);
127 PortGroup::remove_bundle (boost::shared_ptr
<Bundle
> b
)
131 BundleList::iterator i
= _bundles
.begin ();
132 while (i
!= _bundles
.end() && (*i
)->bundle
!= b
) {
136 if (i
== _bundles
.end()) {
147 PortGroup::bundle_changed (Bundle::Change c
)
156 for (BundleList::iterator i
= _bundles
.begin(); i
!= _bundles
.end(); ++i
) {
165 PortGroup::has_port (std::string
const& p
) const
167 for (BundleList::const_iterator i
= _bundles
.begin(); i
!= _bundles
.end(); ++i
) {
168 if ((*i
)->bundle
->offers_port_alone (p
)) {
176 boost::shared_ptr
<Bundle
>
177 PortGroup::only_bundle ()
179 assert (_bundles
.size() == 1);
180 return _bundles
.front()->bundle
;
185 PortGroup::total_channels () const
188 for (BundleList::const_iterator i
= _bundles
.begin(); i
!= _bundles
.end(); ++i
) {
189 n
+= (*i
)->bundle
->nchannels ();
195 boost::shared_ptr
<IO
>
196 PortGroup::io_from_bundle (boost::shared_ptr
<ARDOUR::Bundle
> b
) const
198 BundleList::const_iterator i
= _bundles
.begin ();
199 while (i
!= _bundles
.end() && (*i
)->bundle
!= b
) {
203 if (i
== _bundles
.end()) {
204 return boost::shared_ptr
<IO
> ();
210 /** Remove bundles whose channels are already represented by other, larger bundles */
212 PortGroup::remove_duplicates ()
214 BundleList::iterator i
= _bundles
.begin();
215 while (i
!= _bundles
.end()) {
217 BundleList::iterator tmp
= i
;
222 for (BundleList::iterator j
= _bundles
.begin(); j
!= _bundles
.end(); ++j
) {
224 if ((*j
)->bundle
->nchannels() > (*i
)->bundle
->nchannels()) {
225 /* this bundle is larger */
228 while (k
< (*i
)->bundle
->nchannels().n_total()) {
229 /* see if this channel on *i has an equivalent on *j */
231 while (l
< (*j
)->bundle
->nchannels().n_total() && (*i
)->bundle
->channel_ports (k
) != (*j
)->bundle
->channel_ports (l
)) {
235 if (l
== (*j
)->bundle
->nchannels().n_total()) {
243 if (k
== (*i
)->bundle
->nchannels().n_total()) {
244 /* all channels on *i are represented by the larger bundle *j, so remove *i */
260 /** PortGroupList constructor.
262 PortGroupList::PortGroupList ()
263 : _signals_suspended (false), _pending_change (false), _pending_bundle_change ((Bundle::Change
) 0)
268 PortGroupList::~PortGroupList()
270 /* XXX need to clean up bundles, but ownership shared with PortGroups */
274 PortGroupList::maybe_add_processor_to_list (
275 boost::weak_ptr
<Processor
> wp
, list
<boost::shared_ptr
<IO
> >* route_ios
, bool inputs
, set
<boost::shared_ptr
<IO
> >& used_io
278 boost::shared_ptr
<Processor
> p (wp
.lock());
284 boost::shared_ptr
<IOProcessor
> iop
= boost::dynamic_pointer_cast
<IOProcessor
> (p
);
288 boost::shared_ptr
<IO
> io
= inputs
? iop
->input() : iop
->output();
290 if (io
&& used_io
.find (io
) == used_io
.end()) {
291 route_ios
->push_back (io
);
298 RouteIOs (boost::shared_ptr
<Route
> r
, boost::shared_ptr
<IO
> i
) {
303 boost::shared_ptr
<Route
> route
;
304 std::list
<boost::shared_ptr
<IO
> > ios
;
307 class RouteIOsComparator
{
309 bool operator() (RouteIOs
const & a
, RouteIOs
const & b
) {
310 return a
.route
->order_key (X_("editor")) < b
.route
->order_key (X_("editor"));
314 /** Gather bundles from around the system and put them in this PortGroupList.
315 * @param type Type of bundles to collect, or NIL for all types.
318 PortGroupList::gather (ARDOUR::Session
* session
, ARDOUR::DataType type
, bool inputs
, bool allow_dups
)
326 boost::shared_ptr
<PortGroup
> bus (new PortGroup (_("Bus")));
327 boost::shared_ptr
<PortGroup
> track (new PortGroup (_("Track")));
328 boost::shared_ptr
<PortGroup
> system (new PortGroup (_("System")));
329 boost::shared_ptr
<PortGroup
> ardour (new PortGroup (_("Ardour")));
330 boost::shared_ptr
<PortGroup
> other (new PortGroup (_("Other")));
332 /* Find the IOs which have bundles for routes and their processors. We store
333 these IOs in a RouteIOs class so that we can then sort the results by route
337 boost::shared_ptr
<RouteList
> routes
= session
->get_routes ();
338 list
<RouteIOs
> route_ios
;
340 for (RouteList::const_iterator i
= routes
->begin(); i
!= routes
->end(); ++i
) {
342 /* keep track of IOs that we have taken bundles from,
343 so that we can avoid taking the same IO from both
344 Route::output() and the main_outs Delivery */
346 set
<boost::shared_ptr
<IO
> > used_io
;
347 boost::shared_ptr
<IO
> io
= inputs
? (*i
)->input() : (*i
)->output();
350 RouteIOs
rb (*i
, io
);
351 (*i
)->foreach_processor (boost::bind (&PortGroupList::maybe_add_processor_to_list
, this, _1
, &rb
.ios
, inputs
, used_io
));
353 route_ios
.push_back (rb
);
356 /* Sort RouteIOs by the routes' editor order keys */
357 route_ios
.sort (RouteIOsComparator ());
359 /* Now put the bundles that belong to these sorted RouteIOs into the PortGroup */
360 for (list
<RouteIOs
>::iterator i
= route_ios
.begin(); i
!= route_ios
.end(); ++i
) {
361 TimeAxisView
* tv
= PublicEditor::instance().axis_view_from_route (i
->route
);
363 /* Work out which group to put these IOs' bundles in */
364 boost::shared_ptr
<PortGroup
> g
;
365 if (boost::dynamic_pointer_cast
<Track
> (i
->route
)) {
371 for (list
<boost::shared_ptr
<IO
> >::iterator j
= i
->ios
.begin(); j
!= i
->ios
.end(); ++j
) {
373 g
->add_bundle ((*j
)->bundle(), *j
, tv
->color ());
375 g
->add_bundle ((*j
)->bundle(), *j
);
380 /* Bundles owned by the session; add user bundles first, then normal ones, so
381 that UserBundles that offer the same ports as a normal bundle get priority
384 boost::shared_ptr
<BundleList
> b
= session
->bundles ();
386 for (BundleList::iterator i
= b
->begin(); i
!= b
->end(); ++i
) {
387 if (boost::dynamic_pointer_cast
<UserBundle
> (*i
) && (*i
)->ports_are_inputs() == inputs
) {
388 system
->add_bundle (*i
, allow_dups
);
392 for (BundleList::iterator i
= b
->begin(); i
!= b
->end(); ++i
) {
393 if (boost::dynamic_pointer_cast
<UserBundle
> (*i
) == 0 && (*i
)->ports_are_inputs() == inputs
) {
394 system
->add_bundle (*i
, allow_dups
);
400 if (!inputs
&& (type
== DataType::AUDIO
|| type
== DataType::NIL
)) {
401 ardour
->add_bundle (session
->the_auditioner()->output()->bundle());
402 ardour
->add_bundle (session
->click_io()->bundle());
405 /* Now find all other ports that we haven't thought of yet */
407 std::vector
<std::string
> extra_system
[DataType::num_types
];
408 std::vector
<std::string
> extra_other
[DataType::num_types
];
410 const char ** ports
= 0;
411 if (type
== DataType::NIL
) {
412 ports
= session
->engine().get_ports ("", "", inputs
? JackPortIsInput
: JackPortIsOutput
);
414 ports
= session
->engine().get_ports ("", type
.to_jack_type(), inputs
? JackPortIsInput
: JackPortIsOutput
);
423 std::string
const p
= ports
[n
];
425 if (!system
->has_port(p
) &&
427 !track
->has_port(p
) &&
428 !ardour
->has_port(p
) &&
429 !other
->has_port(p
)) {
431 /* special hack: ignore MIDI ports labelled Midi-Through. these
432 are basically useless and mess things up for default
436 if (p
.find ("MIDI-Through") != string::npos
) {
441 /* can't use the audio engine for this as we are looking at non-Ardour ports */
443 jack_port_t
* jp
= jack_port_by_name (session
->engine().jack(), p
.c_str());
445 DataType
t (jack_port_type (jp
));
446 if (t
!= DataType::NIL
) {
447 if (port_has_prefix (p
, "system:") || port_has_prefix (p
, "alsa_pcm") || port_has_prefix (p
, "ardour:")) {
448 extra_system
[t
].push_back (p
);
450 extra_other
[t
].push_back (p
);
462 for (DataType::iterator i
= DataType::begin(); i
!= DataType::end(); ++i
) {
463 if (!extra_system
[*i
].empty()) {
464 system
->add_bundle (make_bundle_from_ports (extra_system
[*i
], *i
, inputs
));
468 for (DataType::iterator i
= DataType::begin(); i
!= DataType::end(); ++i
) {
469 if (!extra_other
[*i
].empty()) {
470 other
->add_bundle (make_bundle_from_ports (extra_other
[*i
], *i
, inputs
));
475 system
->remove_duplicates ();
478 add_group_if_not_empty (other
);
479 add_group_if_not_empty (bus
);
480 add_group_if_not_empty (track
);
481 add_group_if_not_empty (ardour
);
482 add_group_if_not_empty (system
);
487 boost::shared_ptr
<Bundle
>
488 PortGroupList::make_bundle_from_ports (std::vector
<std::string
> const & p
, ARDOUR::DataType type
, bool inputs
) const
490 boost::shared_ptr
<Bundle
> b (new Bundle ("", inputs
));
492 std::string
const pre
= common_prefix (p
);
494 b
->set_name (pre
.substr (0, pre
.length() - 1));
497 for (uint32_t j
= 0; j
< p
.size(); ++j
) {
498 b
->add_channel (p
[j
].substr (pre
.length()), type
);
499 b
->set_port (j
, p
[j
]);
506 PortGroupList::port_has_prefix (const std::string
& n
, const std::string
& p
) const
508 return n
.substr (0, p
.length()) == p
;
512 PortGroupList::common_prefix_before (std::vector
<std::string
> const & p
, std::string
const & s
) const
514 /* we must have some strings and the first must contain the separator string */
515 if (p
.empty() || p
[0].find_first_of (s
) == std::string::npos
) {
519 /* prefix of the first string */
520 std::string
const fp
= p
[0].substr (0, p
[0].find_first_of (s
) + 1);
522 /* see if the other strings also start with fp */
524 while (j
< p
.size()) {
525 if (p
[j
].substr (0, fp
.length()) != fp
) {
540 PortGroupList::common_prefix (std::vector
<std::string
> const & p
) const
542 /* common prefix before '/' ? */
543 std::string cp
= common_prefix_before (p
, "/");
548 cp
= common_prefix_before (p
, ":");
557 PortGroupList::clear ()
560 _bundle_changed_connections
.drop_connections ();
565 PortGroup::BundleList
const &
566 PortGroupList::bundles () const
570 for (PortGroupList::List::const_iterator i
= begin (); i
!= end (); ++i
) {
571 std::copy ((*i
)->bundles().begin(), (*i
)->bundles().end(), std::back_inserter (_bundles
));
578 PortGroupList::total_channels () const
582 for (PortGroupList::List::const_iterator i
= begin(); i
!= end(); ++i
) {
583 n
+= (*i
)->total_channels ();
590 PortGroupList::add_group_if_not_empty (boost::shared_ptr
<PortGroup
> g
)
592 if (!g
->bundles().empty ()) {
598 PortGroupList::add_group (boost::shared_ptr
<PortGroup
> g
)
600 _groups
.push_back (g
);
602 g
->Changed
.connect (_changed_connections
, invalidator (*this), boost::bind (&PortGroupList::emit_changed
, this), gui_context());
603 g
->BundleChanged
.connect (_bundle_changed_connections
, invalidator (*this), ui_bind (&PortGroupList::emit_bundle_changed
, this, _1
), gui_context());
609 PortGroupList::remove_bundle (boost::shared_ptr
<Bundle
> b
)
611 for (List::iterator i
= _groups
.begin(); i
!= _groups
.end(); ++i
) {
612 (*i
)->remove_bundle (b
);
619 PortGroupList::emit_changed ()
621 if (_signals_suspended
) {
622 _pending_change
= true;
629 PortGroupList::emit_bundle_changed (Bundle::Change c
)
631 if (_signals_suspended
) {
632 _pending_bundle_change
= c
;
638 PortGroupList::suspend_signals ()
640 _signals_suspended
= true;
644 PortGroupList::resume_signals ()
646 if (_pending_change
) {
648 _pending_change
= false;
651 if (_pending_bundle_change
!= 0) {
652 BundleChanged (_pending_bundle_change
);
653 _pending_bundle_change
= (ARDOUR::Bundle::Change
) 0;
656 _signals_suspended
= false;
659 boost::shared_ptr
<IO
>
660 PortGroupList::io_from_bundle (boost::shared_ptr
<ARDOUR::Bundle
> b
) const
662 List::const_iterator i
= _groups
.begin ();
663 while (i
!= _groups
.end()) {
664 boost::shared_ptr
<IO
> io
= (*i
)->io_from_bundle (b
);
671 return boost::shared_ptr
<IO
> ();
675 PortGroupList::empty () const
677 List::const_iterator i
= _groups
.begin ();
678 while (i
!= _groups
.end() && (*i
)->total_channels() == ChanCount::ZERO
) {
682 return (i
== _groups
.end());