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