drastic, deep and wide changes to make RouteGroup use boost::shared_ptr<Route> and...
[ardour.git] / gtk2_ardour / port_group.cc
1 /*
2     Copyright (C) 2002-2009 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <cstring>
21 #include <boost/shared_ptr.hpp>
22
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"
32
33 #include "port_group.h"
34 #include "port_matrix.h"
35 #include "time_axis_view.h"
36 #include "public_editor.h"
37
38 #include "i18n.h"
39
40 using namespace std;
41 using namespace Gtk;
42 using namespace ARDOUR;
43
44 /** PortGroup constructor.
45  * @param n Name.
46  */
47 PortGroup::PortGroup (std::string const & n)
48         : name (n)
49 {
50
51 }
52
53 /** Add a bundle to a group.
54  *  @param b Bundle.
55  *  @param allow_dups true to allow the group to contain more than one bundle with the same port, otherwise false.
56  */
57 void
58 PortGroup::add_bundle (boost::shared_ptr<Bundle> b, bool allow_dups)
59 {
60         add_bundle_internal (b, boost::shared_ptr<IO> (), false, Gdk::Color (), allow_dups);
61 }
62
63 /** Add a bundle to a group.
64  *  @param b Bundle.
65  *  @param io IO whose ports are in the bundle.
66  */
67 void
68 PortGroup::add_bundle (boost::shared_ptr<Bundle> b, boost::shared_ptr<IO> io)
69 {
70         add_bundle_internal (b, io, false, Gdk::Color (), false);
71 }
72
73 /** Add a bundle to a group.
74  *  @param b Bundle.
75  *  @param c Colour to represent the bundle with.
76  */
77 void
78 PortGroup::add_bundle (boost::shared_ptr<Bundle> b, boost::shared_ptr<IO> io, Gdk::Color c)
79 {
80         add_bundle_internal (b, io, true, c, false);
81 }
82
83 void
84 PortGroup::add_bundle_internal (boost::shared_ptr<Bundle> b, boost::shared_ptr<IO> io, bool has_colour, Gdk::Color colour, bool allow_dups)
85 {
86         assert (b.get());
87
88         if (!allow_dups) {
89                 
90                 /* don't add this bundle if we already have one with the same ports */
91                 
92                 BundleList::iterator i = _bundles.begin ();
93                 while (i != _bundles.end() && b->has_same_ports (i->bundle) == false) {
94                         ++i;
95                 }
96                 
97                 if (i != _bundles.end ()) {
98                         return;
99                 }
100         }
101
102         BundleRecord r;
103         r.bundle = b;
104         r.io = io;
105         r.colour = colour;
106         r.has_colour = has_colour;
107         r.changed_connection = b->Changed.connect (sigc::mem_fun (*this, &PortGroup::bundle_changed));
108
109         _bundles.push_back (r);
110
111         Changed ();     
112 }
113
114 void
115 PortGroup::remove_bundle (boost::shared_ptr<Bundle> b)
116 {
117         assert (b.get());
118
119         BundleList::iterator i = _bundles.begin ();
120         while (i != _bundles.end() && i->bundle != b) {
121                 ++i;
122         }
123
124         if (i == _bundles.end()) {
125                 return;
126         }
127
128         i->changed_connection.disconnect ();
129         _bundles.erase (i);
130
131         Changed ();
132 }
133
134 void
135 PortGroup::bundle_changed (Bundle::Change c)
136 {
137         BundleChanged (c);
138 }
139
140
141 void
142 PortGroup::clear ()
143 {
144         for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
145                 i->changed_connection.disconnect ();
146         }
147
148         _bundles.clear ();
149         Changed ();
150 }
151
152 bool
153 PortGroup::has_port (std::string const& p) const
154 {
155         for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
156                 if (i->bundle->offers_port_alone (p)) {
157                         return true;
158                 }
159         }
160
161         return false;
162 }
163
164 boost::shared_ptr<Bundle>
165 PortGroup::only_bundle ()
166 {
167         assert (_bundles.size() == 1);
168         return _bundles.front().bundle;
169 }
170
171
172 uint32_t
173 PortGroup::total_channels () const
174 {
175         uint32_t n = 0;
176         for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
177                 n += i->bundle->nchannels ();
178         }
179
180         return n;
181 }
182
183 boost::shared_ptr<IO>
184 PortGroup::io_from_bundle (boost::shared_ptr<ARDOUR::Bundle> b) const
185 {
186         BundleList::const_iterator i = _bundles.begin ();
187         while (i != _bundles.end() && i->bundle != b) {
188                 ++i;
189         }
190
191         if (i == _bundles.end()) {
192                 return boost::shared_ptr<IO> ();
193         }
194
195         return i->io;
196 }
197
198
199 /** PortGroupList constructor.
200  */
201 PortGroupList::PortGroupList ()
202         : _type (DataType::AUDIO), _signals_suspended (false), _pending_change (false), _pending_bundle_change ((Bundle::Change) 0)
203 {
204
205 }
206
207 void
208 PortGroupList::set_type (DataType t)
209 {
210         _type = t;
211         clear ();
212 }
213
214 void
215 PortGroupList::maybe_add_processor_to_list (
216         boost::weak_ptr<Processor> wp, list<boost::shared_ptr<Bundle> >* route_bundles, bool inputs, set<boost::shared_ptr<IO> >& used_io
217         )
218 {
219         boost::shared_ptr<Processor> p (wp.lock());
220
221         if (!p) {
222                 return;
223         }
224
225         boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (p);
226
227         if (iop) {
228
229                 boost::shared_ptr<IO> io = inputs ? iop->input() : iop->output();
230
231                 if (io && used_io.find (io) == used_io.end()) {
232                         route_bundles->push_back (io->bundle ());
233                         used_io.insert (io);
234                 }
235         }
236 }
237
238
239 /** Gather bundles from around the system and put them in this PortGroupList */
240 void
241 PortGroupList::gather (ARDOUR::Session* session, bool inputs, bool allow_dups)
242 {
243         clear ();
244
245         if (session == 0) {
246                 return;
247         }
248
249         boost::shared_ptr<PortGroup> bus (new PortGroup (_("Bus")));
250         boost::shared_ptr<PortGroup> track (new PortGroup (_("Track")));
251         boost::shared_ptr<PortGroup> system (new PortGroup (_("System")));
252         boost::shared_ptr<PortGroup> ardour (new PortGroup (_("Ardour")));
253         boost::shared_ptr<PortGroup> other (new PortGroup (_("Other")));
254
255         /* Find the bundles for routes.  We use the RouteBundle class to join
256            the route's input/output and processor bundles together so that they
257            are presented as one bundle in the matrix. */
258
259         boost::shared_ptr<RouteList> routes = session->get_routes ();
260
261         for (RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
262
263                 list<boost::shared_ptr<Bundle> > route_bundles;
264
265                 /* keep track of IOs that we have taken bundles from,
266                    so that we can avoid taking the same IO from both
267                    Route::output() and the main_outs Delivery */
268
269                 set<boost::shared_ptr<IO> > used_io;
270                 boost::shared_ptr<IO> io = inputs ? (*i)->input() : (*i)->output();
271                 used_io.insert (io);
272
273                 route_bundles.push_back (io->bundle ());
274
275                 (*i)->foreach_processor (bind (mem_fun (*this, &PortGroupList::maybe_add_processor_to_list), &route_bundles, inputs, used_io));
276
277                 /* Work out which group to put these bundles in */
278                 boost::shared_ptr<PortGroup> g;
279                 if (_type == DataType::AUDIO) {
280
281                         if (boost::dynamic_pointer_cast<AudioTrack> (*i)) {
282                                 g = track;
283                         } else if (!boost::dynamic_pointer_cast<MidiTrack>(*i)) {
284                                 g = bus;
285                         }
286
287
288                 } else if (_type == DataType::MIDI) {
289
290                         if (boost::dynamic_pointer_cast<MidiTrack> (*i)) {
291                                 g = track;
292                         }
293
294                         /* No MIDI busses yet */
295                 }
296
297                 if (g) {
298
299                         TimeAxisView* tv = PublicEditor::instance().axis_view_from_route (*i);
300                         for (list<boost::shared_ptr<Bundle> >::iterator i = route_bundles.begin(); i != route_bundles.end(); ++i) {
301                                 if (tv) {
302                                         g->add_bundle (*i, io, tv->color ());
303                                 } else {
304                                         g->add_bundle (*i, io);
305                                 }
306                         }
307                 }
308         }
309
310         /* Bundles owned by the session; add user bundles first, then normal ones, so
311            that UserBundles that offer the same ports as a normal bundle get priority
312         */
313
314         boost::shared_ptr<BundleList> b = session->bundles ();
315
316         for (BundleList::iterator i = b->begin(); i != b->end(); ++i) {
317                 if (boost::dynamic_pointer_cast<UserBundle> (*i) && (*i)->ports_are_inputs() == inputs && (*i)->type() == _type) {
318                         system->add_bundle (*i, allow_dups);
319                 }
320         }
321
322         for (BundleList::iterator i = b->begin(); i != b->end(); ++i) {
323                 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0 && (*i)->ports_are_inputs() == inputs && (*i)->type() == _type) {
324                         system->add_bundle (*i, allow_dups);
325                 }
326         }
327         
328         /* Ardour stuff */
329
330         if (!inputs && _type == DataType::AUDIO) {
331                 ardour->add_bundle (session->the_auditioner()->output()->bundle());
332                 ardour->add_bundle (session->click_io()->bundle());
333         }
334
335         /* Now find all other ports that we haven't thought of yet */
336
337         std::vector<std::string> extra_system;
338         std::vector<std::string> extra_other;
339
340         const char **ports = session->engine().get_ports ("", _type.to_jack_type(), inputs ?
341                                                          JackPortIsInput : JackPortIsOutput);
342         if (ports) {
343
344                 int n = 0;
345                 string client_matching_string;
346
347                 client_matching_string = session->engine().client_name();
348                 client_matching_string += ':';
349
350                 while (ports[n]) {
351
352                         std::string const p = ports[n];
353
354                         cout << p << "\n";
355
356                         if (!system->has_port(p) &&
357                             !bus->has_port(p) &&
358                             !track->has_port(p) &&
359                             !ardour->has_port(p) &&
360                             !other->has_port(p)) {
361
362                                 if (port_has_prefix (p, "system:") ||
363                                     port_has_prefix (p, "alsa_pcm") ||
364                                     port_has_prefix (p, "ardour:")) {
365                                         extra_system.push_back (p);
366                                 } else {
367                                         extra_other.push_back (p);
368                                 }
369                         }
370
371                         ++n;
372                 }
373
374                 free (ports);
375         }
376
377         if (!extra_system.empty()) {
378                 boost::shared_ptr<Bundle> b = make_bundle_from_ports (extra_system, inputs);
379                 system->add_bundle (b);
380         }
381
382         if (!extra_other.empty()) {
383                 other->add_bundle (make_bundle_from_ports (extra_other, inputs));
384         }
385
386         add_group_if_not_empty (system);
387         add_group_if_not_empty (bus);
388         add_group_if_not_empty (track);
389         add_group_if_not_empty (ardour);
390         add_group_if_not_empty (other);
391
392         emit_changed ();
393 }
394
395 boost::shared_ptr<Bundle>
396 PortGroupList::make_bundle_from_ports (std::vector<std::string> const & p, bool inputs) const
397 {
398         boost::shared_ptr<Bundle> b (new Bundle ("", _type, inputs));
399
400         std::string const pre = common_prefix (p);
401         if (!pre.empty()) {
402                 b->set_name (pre.substr (0, pre.length() - 1));
403         }
404
405         for (uint32_t j = 0; j < p.size(); ++j) {
406                 b->add_channel (p[j].substr (pre.length()));
407                 b->set_port (j, p[j]);
408         }
409
410         return b;
411 }
412
413 bool
414 PortGroupList::port_has_prefix (const std::string& n, const std::string& p) const
415 {
416         return n.substr (0, p.length()) == p;
417 }
418
419 std::string
420 PortGroupList::common_prefix_before (std::vector<std::string> const & p, std::string const & s) const
421 {
422         /* we must have some strings and the first must contain the separator string */
423         if (p.empty() || p[0].find_first_of (s) == std::string::npos) {
424                 return "";
425         }
426
427         /* prefix of the first string */
428         std::string const fp = p[0].substr (0, p[0].find_first_of (s) + 1);
429
430         /* see if the other strings also start with fp */
431         uint32_t j = 1;
432         while (j < p.size()) {
433                 if (p[j].substr (0, fp.length()) != fp) {
434                         break;
435                 }
436                 ++j;
437         }
438
439         if (j != p.size()) {
440                 return "";
441         }
442
443         return fp;
444 }
445
446
447 std::string
448 PortGroupList::common_prefix (std::vector<std::string> const & p) const
449 {
450         /* common prefix before '/' ? */
451         std::string cp = common_prefix_before (p, "/");
452         if (!cp.empty()) {
453                 return cp;
454         }
455
456         cp = common_prefix_before (p, ":");
457         if (!cp.empty()) {
458                 return cp;
459         }
460
461         return "";
462 }
463
464 void
465 PortGroupList::clear ()
466 {
467         _groups.clear ();
468
469         for (std::vector<sigc::connection>::iterator i = _bundle_changed_connections.begin(); i != _bundle_changed_connections.end(); ++i) {
470                 i->disconnect ();
471         }
472
473         _bundle_changed_connections.clear ();
474
475         emit_changed ();
476 }
477
478
479 PortGroup::BundleList const &
480 PortGroupList::bundles () const
481 {
482         _bundles.clear ();
483
484         for (PortGroupList::List::const_iterator i = begin (); i != end (); ++i) {
485                 std::copy ((*i)->bundles().begin(), (*i)->bundles().end(), std::back_inserter (_bundles));
486         }
487
488         return _bundles;
489 }
490
491 uint32_t
492 PortGroupList::total_channels () const
493 {
494         uint32_t n = 0;
495
496         for (PortGroupList::List::const_iterator i = begin(); i != end(); ++i) {
497                 n += (*i)->total_channels ();
498         }
499
500         return n;
501 }
502
503 void
504 PortGroupList::add_group_if_not_empty (boost::shared_ptr<PortGroup> g)
505 {
506         if (!g->bundles().empty ()) {
507                 add_group (g);
508         }
509 }
510
511 void
512 PortGroupList::add_group (boost::shared_ptr<PortGroup> g)
513 {
514         _groups.push_back (g);
515
516         g->Changed.connect (sigc::mem_fun (*this, &PortGroupList::emit_changed));
517
518         _bundle_changed_connections.push_back (
519                 g->BundleChanged.connect (sigc::mem_fun (*this, &PortGroupList::emit_bundle_changed))
520                 );
521
522         emit_changed ();
523 }
524
525 void
526 PortGroupList::remove_bundle (boost::shared_ptr<Bundle> b)
527 {
528         for (List::iterator i = _groups.begin(); i != _groups.end(); ++i) {
529                 (*i)->remove_bundle (b);
530         }
531
532         emit_changed ();
533 }
534
535 void
536 PortGroupList::emit_changed ()
537 {
538         if (_signals_suspended) {
539                 _pending_change = true;
540         } else {
541                 Changed ();
542         }
543 }
544
545 void
546 PortGroupList::emit_bundle_changed (Bundle::Change c)
547 {
548         if (_signals_suspended) {
549                 _pending_bundle_change = c;
550         } else {
551                 BundleChanged (c);
552         }
553 }
554 void
555 PortGroupList::suspend_signals ()
556 {
557         _signals_suspended = true;
558 }
559
560 void
561 PortGroupList::resume_signals ()
562 {
563         if (_pending_change) {
564                 Changed ();
565                 _pending_change = false;
566         }
567
568         if (_pending_bundle_change != 0) {
569                 BundleChanged (_pending_bundle_change);
570                 _pending_bundle_change = (ARDOUR::Bundle::Change) 0;
571         }
572
573         _signals_suspended = false;
574 }
575
576 boost::shared_ptr<IO>
577 PortGroupList::io_from_bundle (boost::shared_ptr<ARDOUR::Bundle> b) const
578 {
579         List::const_iterator i = _groups.begin ();
580         while (i != _groups.end()) {
581                 boost::shared_ptr<IO> io = (*i)->io_from_bundle (b);
582                 if (io) {
583                         return io;
584                 }
585                 ++i;
586         }
587
588         return boost::shared_ptr<IO> ();
589 }
590
591 bool
592 PortGroupList::empty () const
593 {
594         List::const_iterator i = _groups.begin ();
595         while (i != _groups.end() && (*i)->total_channels() == 0) {
596                 ++i;
597         }
598
599         return (i == _groups.end());
600 }
601