fix up logic that connects ControlGroups and RouteGroups
[ardour.git] / libs / ardour / route_group.cc
1 /*
2     Copyright (C) 2000-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 <inttypes.h>
21
22 #include <algorithm>
23
24 #include "pbd/error.h"
25 #include "pbd/enumwriter.h"
26 #include "pbd/strsplit.h"
27 #include "pbd/debug.h"
28
29 #include "ardour/amp.h"
30 #include "ardour/audio_track.h"
31 #include "ardour/monitor_control.h"
32 #include "ardour/route.h"
33 #include "ardour/route_group.h"
34 #include "ardour/session.h"
35
36 #include "i18n.h"
37
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace std;
41
42 namespace ARDOUR {
43         namespace Properties {
44                 PropertyDescriptor<bool> active;
45                 PropertyDescriptor<bool> group_relative;
46                 PropertyDescriptor<bool> group_gain;
47                 PropertyDescriptor<bool> group_mute;
48                 PropertyDescriptor<bool> group_solo;
49                 PropertyDescriptor<bool> group_recenable;
50                 PropertyDescriptor<bool> group_select;
51                 PropertyDescriptor<bool> group_route_active;
52                 PropertyDescriptor<bool> group_color;
53                 PropertyDescriptor<bool> group_monitoring;
54         }
55 }
56
57 void
58 RouteGroup::make_property_quarks ()
59 {
60         Properties::active.property_id = g_quark_from_static_string (X_("active"));
61         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n",      Properties::active.property_id));
62
63         Properties::group_relative.property_id = g_quark_from_static_string (X_("relative"));
64         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n",    Properties::group_relative.property_id));
65         Properties::group_gain.property_id = g_quark_from_static_string (X_("gain"));
66         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n",        Properties::group_gain.property_id));
67         Properties::group_mute.property_id = g_quark_from_static_string (X_("mute"));
68         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n",        Properties::group_mute.property_id));
69         Properties::group_solo.property_id = g_quark_from_static_string (X_("solo"));
70         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n",        Properties::group_solo.property_id));
71         Properties::group_recenable.property_id = g_quark_from_static_string (X_("recenable"));
72         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n",   Properties::group_recenable.property_id));
73         Properties::group_select.property_id = g_quark_from_static_string (X_("select"));
74         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n",      Properties::group_select.property_id));
75         Properties::group_route_active.property_id = g_quark_from_static_string (X_("route-active"));
76         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for route-active = %1\n", Properties::group_route_active.property_id));
77         Properties::group_color.property_id = g_quark_from_static_string (X_("color"));
78         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for color = %1\n",       Properties::group_color.property_id));
79         Properties::group_monitoring.property_id = g_quark_from_static_string (X_("monitoring"));
80         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for monitoring = %1\n",       Properties::group_monitoring.property_id));
81 }
82
83 #define ROUTE_GROUP_DEFAULT_PROPERTIES  _relative (Properties::group_relative, true) \
84         , _active (Properties::active, true) \
85         , _hidden (Properties::hidden, false) \
86         , _gain (Properties::group_gain, true) \
87         , _mute (Properties::group_mute, true) \
88         , _solo (Properties::group_solo, true) \
89         , _recenable (Properties::group_recenable, true) \
90         , _select (Properties::group_select, true) \
91         , _route_active (Properties::group_route_active, true) \
92         , _color (Properties::group_color, true) \
93         , _monitoring (Properties::group_monitoring, true)
94
95 RouteGroup::RouteGroup (Session& s, const string &n)
96         : SessionObject (s, n)
97         , routes (new RouteList)
98         , ROUTE_GROUP_DEFAULT_PROPERTIES
99         , _solo_group (new ControlGroup (SoloAutomation))
100         , _mute_group (new ControlGroup (MuteAutomation))
101         , _rec_enable_group (new ControlGroup (RecEnableAutomation))
102         , _gain_group (new ControlGroup (GainAutomation))
103         , _monitoring_group (new ControlGroup (MonitoringAutomation))
104 {
105         _xml_node_name = X_("RouteGroup");
106
107         add_property (_relative);
108         add_property (_active);
109         add_property (_hidden);
110         add_property (_gain);
111         add_property (_mute);
112         add_property (_solo);
113         add_property (_recenable);
114         add_property (_select);
115         add_property (_route_active);
116         add_property (_color);
117         add_property (_monitoring);
118 }
119
120 RouteGroup::~RouteGroup ()
121 {
122         _solo_group->clear ();
123         _mute_group->clear ();
124         _gain_group->clear ();
125         _rec_enable_group->clear ();
126         _monitoring_group->clear ();
127
128         for (RouteList::iterator i = routes->begin(); i != routes->end();) {
129                 RouteList::iterator tmp = i;
130                 ++tmp;
131
132                 (*i)->set_route_group (0);
133
134                 i = tmp;
135         }
136 }
137
138 /** Add a route to a group.  Adding a route which is already in the group is allowed; nothing will happen.
139  *  @param r Route to add.
140  */
141 int
142 RouteGroup::add (boost::shared_ptr<Route> r)
143 {
144         if (find (routes->begin(), routes->end(), r) != routes->end()) {
145                 return 0;
146         }
147
148         if (r->route_group()) {
149                 r->route_group()->remove (r);
150         }
151
152         routes->push_back (r);
153
154         _solo_group->add_control (r->solo_control());
155         _mute_group->add_control (r->mute_control());
156         _gain_group->add_control (r->gain_control());
157         boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r);
158         if (trk) {
159                 _rec_enable_group->add_control (trk->rec_enable_control());
160                 _monitoring_group->add_control (trk->monitoring_control());
161         }
162
163         r->set_route_group (this);
164         r->DropReferences.connect_same_thread (*this, boost::bind (&RouteGroup::remove_when_going_away, this, boost::weak_ptr<Route> (r)));
165
166         _session.set_dirty ();
167         RouteAdded (this, boost::weak_ptr<Route> (r)); /* EMIT SIGNAL */
168         return 0;
169 }
170
171 void
172 RouteGroup::remove_when_going_away (boost::weak_ptr<Route> wr)
173 {
174         boost::shared_ptr<Route> r (wr.lock());
175
176         if (r) {
177                 remove (r);
178         }
179 }
180
181 int
182 RouteGroup::remove (boost::shared_ptr<Route> r)
183 {
184         RouteList::iterator i;
185
186         if ((i = find (routes->begin(), routes->end(), r)) != routes->end()) {
187                 r->set_route_group (0);
188                 _solo_group->remove_control (r->solo_control());
189                 _mute_group->remove_control (r->mute_control());
190                 _gain_group->remove_control (r->gain_control());
191                 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r);
192                 if (trk) {
193                         _rec_enable_group->remove_control (trk->rec_enable_control());
194                         _monitoring_group->remove_control (trk->monitoring_control());
195                 }
196                 routes->erase (i);
197                 _session.set_dirty ();
198                 RouteRemoved (this, boost::weak_ptr<Route> (r)); /* EMIT SIGNAL */
199                 return 0;
200         }
201
202         return -1;
203 }
204
205
206 XMLNode&
207 RouteGroup::get_state ()
208 {
209         XMLNode *node = new XMLNode ("RouteGroup");
210
211         char buf[64];
212         id().print (buf, sizeof (buf));
213         node->add_property ("id", buf);
214
215         add_properties (*node);
216
217         if (!routes->empty()) {
218                 stringstream str;
219
220                 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
221                         str << (*i)->id () << ' ';
222                 }
223
224                 node->add_property ("routes", str.str());
225         }
226
227         return *node;
228 }
229
230 int
231 RouteGroup::set_state (const XMLNode& node, int version)
232 {
233         if (version < 3000) {
234                 return set_state_2X (node, version);
235         }
236
237         XMLProperty const * prop;
238
239         set_id (node);
240         set_values (node);
241
242         if ((prop = node.property ("routes")) != 0) {
243                 stringstream str (prop->value());
244                 vector<string> ids;
245                 split (str.str(), ids, ' ');
246
247                 for (vector<string>::iterator i = ids.begin(); i != ids.end(); ++i) {
248                         PBD::ID id (*i);
249                         boost::shared_ptr<Route> r = _session.route_by_id (id);
250
251                         if (r) {
252                                 add (r);
253                         }
254                 }
255         }
256
257         push_to_groups ();
258
259         return 0;
260 }
261
262 int
263 RouteGroup::set_state_2X (const XMLNode& node, int /*version*/)
264 {
265         set_values (node);
266
267         if (node.name() == "MixGroup") {
268                 _gain = true;
269                 _mute = true;
270                 _solo = true;
271                 _recenable = true;
272                 _route_active = true;
273                 _color = false;
274         } else if (node.name() == "EditGroup") {
275                 _gain = false;
276                 _mute = false;
277                 _solo = false;
278                 _recenable = false;
279                 _route_active = false;
280                 _color = false;
281         }
282
283         push_to_groups ();
284
285         return 0;
286 }
287
288 void
289 RouteGroup::set_gain (bool yn)
290 {
291         if (is_gain() == yn) {
292                 return;
293         }
294         _gain = yn;
295         _gain_group->set_active (yn);
296
297         send_change (PropertyChange (Properties::group_gain));
298 }
299
300 void
301 RouteGroup::set_mute (bool yn)
302 {
303         if (is_mute() == yn) {
304                 return;
305         }
306         _mute = yn;
307         _mute_group->set_active (yn);
308
309         send_change (PropertyChange (Properties::group_mute));
310 }
311
312 void
313 RouteGroup::set_solo (bool yn)
314 {
315         if (is_solo() == yn) {
316                 return;
317         }
318         _solo = yn;
319         _solo_group->set_active (yn);
320
321         send_change (PropertyChange (Properties::group_solo));
322 }
323
324 void
325 RouteGroup::set_recenable (bool yn)
326 {
327         if (is_recenable() == yn) {
328                 return;
329         }
330         _recenable = yn;
331         _rec_enable_group->set_active (yn);
332         send_change (PropertyChange (Properties::group_recenable));
333 }
334
335 void
336 RouteGroup::set_select (bool yn)
337 {
338         if (is_select() == yn) {
339                 return;
340         }
341         _select = yn;
342         send_change (PropertyChange (Properties::group_select));
343 }
344
345 void
346 RouteGroup::set_route_active (bool yn)
347 {
348         if (is_route_active() == yn) {
349                 return;
350         }
351         _route_active = yn;
352         send_change (PropertyChange (Properties::group_route_active));
353 }
354
355 void
356 RouteGroup::set_color (bool yn)
357 {
358         if (is_color() == yn) {
359                 return;
360         }
361         _color = yn;
362
363         send_change (PropertyChange (Properties::group_color));
364
365         /* This is a bit of a hack, but this might change
366            our route's effective color, so emit gui_changed
367            for our routes.
368         */
369
370         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
371                 (*i)->gui_changed (X_("color"), this);
372         }
373 }
374
375 void
376 RouteGroup::set_monitoring (bool yn)
377 {
378         if (is_monitoring() == yn) {
379                 return;
380         }
381
382         _monitoring = yn;
383         _monitoring_group->set_active (yn);
384
385         send_change (PropertyChange (Properties::group_monitoring));
386
387         _session.set_dirty ();
388 }
389
390 void
391 RouteGroup::set_active (bool yn, void* /*src*/)
392 {
393         if (is_active() == yn) {
394                 return;
395         }
396
397         _active = yn;
398
399         push_to_groups ();
400
401         send_change (PropertyChange (Properties::active));
402         _session.set_dirty ();
403 }
404
405 void
406 RouteGroup::set_relative (bool yn, void* /*src*/)
407 {
408         if (is_relative() == yn) {
409                 return;
410         }
411         _relative = yn;
412         send_change (PropertyChange (Properties::group_relative));
413         _session.set_dirty ();
414 }
415
416 void
417 RouteGroup::set_hidden (bool yn, void* /*src*/)
418 {
419         if (is_hidden() == yn) {
420                 return;
421         }
422
423         if (yn) {
424                 _hidden = true;
425                 if (Config->get_hiding_groups_deactivates_groups()) {
426                         _active = false;
427                 }
428         } else {
429                 _hidden = false;
430                 if (Config->get_hiding_groups_deactivates_groups()) {
431                         _active = true;
432                 }
433         }
434
435         send_change (Properties::hidden);
436
437         _session.set_dirty ();
438 }
439
440 void
441 RouteGroup::audio_track_group (set<boost::shared_ptr<AudioTrack> >& ats)
442 {
443         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
444                 boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(*i);
445                 if (at) {
446                         ats.insert (at);
447                 }
448         }
449 }
450
451 void
452 RouteGroup::make_subgroup (bool aux, Placement placement)
453 {
454         RouteList rl;
455         uint32_t nin = 0;
456
457         /* since we don't do MIDI Busses yet, check quickly ... */
458
459         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
460                 if ((*i)->output()->n_ports().n_midi() != 0) {
461                         PBD::warning << _("You cannot subgroup MIDI tracks at this time") << endmsg;
462                         return;
463                 }
464         }
465
466         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
467                 if (!aux && nin != 0 && nin != (*i)->output()->n_ports().n_audio()) {
468                         PBD::warning << _("You cannot subgroup tracks with different number of outputs at this time.") << endmsg;
469                         return;
470                 }
471                 nin = max (nin, (*i)->output()->n_ports().n_audio());
472         }
473
474         try {
475                 /* use master bus etc. to determine default nouts.
476                  *
477                  * (since tracks can't have fewer outs than ins,
478                  * "nin" currently defines the number of outpus if nin > 2)
479                  */
480                 rl = _session.new_audio_route (nin, 2, 0, 1, string(), PresentationInfo::AudioBus, PresentationInfo::max_order);
481         } catch (...) {
482                 return;
483         }
484
485         subgroup_bus = rl.front();
486         subgroup_bus->set_name (_name);
487
488         if (aux) {
489
490                 _session.add_internal_sends (subgroup_bus, placement, routes);
491
492         } else {
493
494                 boost::shared_ptr<Bundle> bundle = subgroup_bus->input()->bundle ();
495
496                 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
497                         (*i)->output()->disconnect (this);
498                         (*i)->output()->connect_ports_to_bundle (bundle, false, this);
499                 }
500         }
501 }
502
503 void
504 RouteGroup::destroy_subgroup ()
505 {
506         if (!subgroup_bus) {
507                 return;
508         }
509
510         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
511                 (*i)->output()->disconnect (this);
512                 /* XXX find a new bundle to connect to */
513         }
514
515         _session.remove_route (subgroup_bus);
516         subgroup_bus.reset ();
517 }
518
519 bool
520 RouteGroup::has_subgroup() const
521 {
522         return subgroup_bus != 0;
523 }
524
525 bool
526 RouteGroup::enabled_property (PBD::PropertyID prop)
527 {
528         OwnedPropertyList::iterator i = _properties->find (prop);
529         if (i == _properties->end()) {
530                 return false;
531         }
532
533         return dynamic_cast<const PropertyTemplate<bool>* > (i->second)->val ();
534 }
535
536 void
537 RouteGroup::post_set (PBD::PropertyChange const &)
538 {
539         push_to_groups ();
540 }
541
542 void
543 RouteGroup::push_to_groups ()
544 {
545         if (_active) {
546                 _gain_group->set_active (is_gain());
547                 _solo_group->set_active (is_solo());
548                 _mute_group->set_active (is_mute());
549                 _rec_enable_group->set_active (is_recenable());
550                 _monitoring_group->set_active (is_monitoring());
551         } else {
552                 _gain_group->set_active (false);
553                 _solo_group->set_active (false);
554                 _mute_group->set_active (false);
555                 _rec_enable_group->set_active (false);
556                 _monitoring_group->set_active (false);
557         }
558 }
559
560 void
561 RouteGroup::assign_master (boost::shared_ptr<VCA> master)
562 {
563         if (!routes || routes->empty()) {
564                 return;
565         }
566
567         boost::shared_ptr<Route> front = routes->front ();
568
569         if (front->slaved_to (master)) {
570                 return;
571         }
572
573         for (RouteList::iterator r = routes->begin(); r != routes->end(); ++r) {
574                 (*r)->assign (master);
575         }
576 }
577
578 void
579 RouteGroup::unassign_master (boost::shared_ptr<VCA> master)
580 {
581         if (!routes || routes->empty()) {
582                 return;
583         }
584
585         boost::shared_ptr<Route> front = routes->front ();
586
587         if (!front->slaved_to (master)) {
588                 return;
589         }
590
591         for (RouteList::iterator r = routes->begin(); r != routes->end(); ++r) {
592                 (*r)->unassign (master);
593         }
594 }
595
596 bool
597 RouteGroup::slaved () const
598 {
599         if (!routes || routes->empty()) {
600                 return false;
601         }
602
603         return routes->front()->slaved ();
604 }