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