first pass at internal sends. this is a very tentative work in progress, and it is...
[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 "port_group.h"
21 #include "port_matrix.h"
22 #include "i18n.h"
23 #include "ardour/session.h"
24 #include "ardour/audio_track.h"
25 #include "ardour/midi_track.h"
26 #include "ardour/audioengine.h"
27 #include "ardour/port.h"
28 #include "ardour/bundle.h"
29 #include <boost/shared_ptr.hpp>
30 #include <cstring>
31
32 using namespace std;
33 using namespace Gtk;
34
35 /** Add a bundle to a group.
36  *  @param b Bundle.
37  */
38 void
39 PortGroup::add_bundle (boost::shared_ptr<ARDOUR::Bundle> b)
40 {
41         assert (b.get());
42         _bundles.push_back (b);
43 }
44
45 /** Add a port to a group.
46  *  @param p Port.
47  */
48 void
49 PortGroup::add_port (std::string const &p)
50 {
51         ports.push_back (p);
52 }
53
54 void
55 PortGroup::clear ()
56 {
57         _bundles.clear ();
58         ports.clear ();
59 }
60
61 bool
62 PortGroup::has_port (std::string const& p) const
63 {
64         for (ARDOUR::BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
65                 if ((*i)->offers_port_alone (p)) {
66                         return true;
67                 }
68         }
69
70         for (vector<std::string>::const_iterator i = ports.begin(); i != ports.end(); ++i) {
71                 if (*i == p) {
72                         return true;
73                 }
74         }
75
76         return false;
77 }
78
79 boost::shared_ptr<ARDOUR::Bundle>
80 PortGroup::only_bundle ()
81 {
82         assert (_bundles.size() == 1);
83         return _bundles.front();
84 }
85         
86
87 /** PortGroupUI constructor.
88  *  @param m PortMatrix to work for.
89  *  @Param g PortGroup to represent.
90  */
91
92 PortGroupUI::PortGroupUI (PortMatrix* m, PortGroup* g)
93         : _port_matrix (m)
94         , _port_group (g)
95         , _visibility_checkbutton (g->name)
96 {
97         _port_group->set_visible (true);
98         setup_visibility_checkbutton ();
99         
100         _visibility_checkbutton.signal_toggled().connect (sigc::mem_fun (*this, &PortGroupUI::visibility_checkbutton_toggled));
101 }
102
103 /** The visibility of a PortGroupUI has been toggled */
104 void
105 PortGroupUI::visibility_checkbutton_toggled ()
106 {
107         _port_group->set_visible (_visibility_checkbutton.get_active ());
108         setup_visibility_checkbutton ();
109         _port_matrix->setup ();
110 }
111
112 /** Set up the visibility checkbutton according to PortGroup::visible */
113 void
114 PortGroupUI::setup_visibility_checkbutton ()
115 {
116         if (_visibility_checkbutton.get_active () != _port_group->visible()) {
117                 _visibility_checkbutton.set_active (_port_group->visible());
118         }
119 }
120
121 /** PortGroupList constructor.
122  *  @param type Type of bundles to offer (audio or MIDI)
123  *  @param offer_inputs true to offer output bundles, otherwise false.
124  */
125
126 PortGroupList::PortGroupList (ARDOUR::DataType type, bool offer_inputs)
127         : _type (type), _offer_inputs (offer_inputs), _bundles_dirty (true),
128           _buss (_("Bus"), true),
129           _track (_("Track"), true),
130           _system (_("System"), true),
131           _other (_("Other"), true)
132 {
133         
134 }
135
136 /** Gather bundles from around the system and put them in this PortGroupList */
137 void
138 PortGroupList::gather (ARDOUR::Session& session)
139 {
140         clear_list ();
141
142         /* Find the bundles for routes.  We take their bundles, copy them,
143            and add ports from the route's processors */
144
145         boost::shared_ptr<ARDOUR::RouteList> routes = session.get_routes ();
146
147         for (ARDOUR::RouteList::const_iterator i = routes->begin(); i != routes->end(); ++i) {
148                 /* Copy the appropriate bundle from the route */
149                 boost::shared_ptr<ARDOUR::Bundle> bundle (
150                         new ARDOUR::Bundle (
151                                 _offer_inputs ? (*i)->bundle_for_inputs() : (*i)->bundle_for_outputs ()
152                                 )
153                         );
154
155                 /* Add ports from the route's processors */
156                 uint32_t n = 0;
157                 while (1) {
158                         boost::shared_ptr<ARDOUR::Processor> p = (*i)->nth_processor (n);
159                         if (p == 0) {
160                                 break;
161                         }
162
163                         boost::shared_ptr<ARDOUR::IOProcessor> iop = boost::dynamic_pointer_cast<ARDOUR::IOProcessor> (p);
164
165                         if (iop) {
166                                 boost::shared_ptr<ARDOUR::Bundle> pb = _offer_inputs ?
167                                         iop->io()->bundle_for_inputs() : iop->io()->bundle_for_outputs();
168                                 bundle->add_channels_from_bundle (pb);
169                         }
170
171                         ++n;
172                 }
173                         
174                 /* Work out which group to put this bundle in */
175                 PortGroup* g = 0;
176                 if (_type == ARDOUR::DataType::AUDIO) {
177
178                         if (boost::dynamic_pointer_cast<ARDOUR::AudioTrack> (*i)) {
179                                 g = &_track;
180                         } else if (!boost::dynamic_pointer_cast<ARDOUR::MidiTrack>(*i)) {
181                                 g = &_buss;
182                         } 
183
184
185                 } else if (_type == ARDOUR::DataType::MIDI) {
186
187                         if (boost::dynamic_pointer_cast<ARDOUR::MidiTrack> (*i)) {
188                                 g = &_track;
189                         }
190
191                         /* No MIDI busses yet */
192                 } 
193                         
194                 if (g) {
195                         g->add_bundle (bundle);
196                 }
197         }
198
199         /* Bundles created by the session */
200         
201         boost::shared_ptr<ARDOUR::BundleList> b = session.bundles ();
202         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
203                 if ((*i)->ports_are_inputs() == _offer_inputs && (*i)->type() == _type) {
204                         _system.add_bundle (*i);
205                 }
206         }
207
208         /* Now find all other ports that we haven't thought of yet */
209
210         const char **ports = session.engine().get_ports ("", _type.to_jack_type(), _offer_inputs ? 
211                                                           JackPortIsInput : JackPortIsOutput);
212         if (ports) {
213
214                 int n = 0;
215                 string client_matching_string;
216
217                 client_matching_string = session.engine().client_name();
218                 client_matching_string += ':';
219
220                 while (ports[n]) {
221                         
222                         std::string const p = ports[n];
223
224                         if (!_system.has_port(p) && !_buss.has_port(p) && !_track.has_port(p) && !_other.has_port(p)) {
225                                 
226                                 if (port_has_prefix (p, "system:") ||
227                                     port_has_prefix (p, "alsa_pcm") ||
228                                     port_has_prefix (p, "ardour:")) {
229                                         _system.add_port (p);
230                                 } else {
231                                         _other.add_port (p);
232                                 }
233                         }
234                         
235                         ++n;
236                 }
237
238                 free (ports);
239         }
240
241         push_back (&_system);
242         push_back (&_buss);
243         push_back (&_track);
244         push_back (&_other);
245
246         for (iterator i = begin(); i != end(); ++i) {
247                 _visibility_connections.push_back (
248                         (*i)->VisibilityChanged.connect (sigc::mem_fun (*this, &PortGroupList::visibility_changed))
249                         );
250         }
251
252         _bundles_dirty = true;
253 }
254
255 bool
256 PortGroupList::port_has_prefix (const std::string& n, const std::string& p) const
257 {
258         return n.substr (0, p.length()) == p;
259 }
260         
261
262 void
263 PortGroupList::set_type (ARDOUR::DataType t)
264 {
265         _type = t;
266         _bundles_dirty = true;
267 }
268
269 void
270 PortGroupList::set_offer_inputs (bool i)
271 {
272         _offer_inputs = i;
273         _bundles_dirty = true;
274 }
275
276 void
277 PortGroupList::update_bundles () const
278 {
279         _bundles.clear ();
280                 
281         for (const_iterator i = begin (); i != end (); ++i) {
282                 if ((*i)->visible()) {
283                         
284                         std::copy ((*i)->bundles().begin(), (*i)->bundles().end(), std::back_inserter (_bundles));
285
286                         /* make a bundle for the ports, if there are any */
287                         if (!(*i)->ports.empty()) {
288
289                                 boost::shared_ptr<ARDOUR::Bundle> b (new ARDOUR::Bundle ("", _type, !_offer_inputs));
290                                 
291                                 std::string const pre = common_prefix ((*i)->ports);
292                                 if (!pre.empty()) {
293                                         b->set_name (pre.substr (0, pre.length() - 1));
294                                 }
295
296                                 for (uint32_t j = 0; j < (*i)->ports.size(); ++j) {
297                                         std::string const p = (*i)->ports[j];
298                                         b->add_channel (p.substr (pre.length()));
299                                         b->set_port (j, p);
300                                 }
301                                         
302                                 _bundles.push_back (b);
303                         }
304                 }
305         }
306
307         _bundles_dirty = false;
308 }
309
310 std::string
311 PortGroupList::common_prefix (std::vector<std::string> const & p) const
312 {
313         /* common prefix before '/' ? */
314         if (p[0].find_first_of ("/") != std::string::npos) {
315                 std::string const fp = p[0].substr (0, (p[0].find_first_of ("/") + 1));
316                 uint32_t j = 1;
317                 while (j < p.size()) {
318                         if (p[j].substr (0, fp.length()) != fp) {
319                                 break;
320                         }
321                         ++j;
322                 }
323                 
324                 if (j == p.size()) {
325                         return fp;
326                 }
327         }
328         
329         /* or before ':' ? */
330         if (p[0].find_first_of (":") != std::string::npos) {
331                 std::string const fp = p[0].substr (0, (p[0].find_first_of (":") + 1));
332                 uint32_t j = 1;
333                 while (j < p.size()) {
334                         if (p[j].substr (0, fp.length()) != fp) {
335                                 break;
336                         }
337                         ++j;
338                 }
339                 
340                 if (j == p.size()) {
341                         return fp;
342                 }
343         }
344
345         return "";
346 }
347
348 void
349 PortGroupList::visibility_changed ()
350 {
351         VisibilityChanged ();
352 }
353
354 void
355 PortGroupList::take_visibility_from (PortGroupList const & o)
356 {
357         iterator i = begin ();
358         const_iterator j = o.begin ();
359
360         while (i != end() && j != o.end()) {
361                 (*i)->set_visible ((*j)->visible());
362                 ++i;
363                 ++j;
364         }
365 }
366
367 void
368 PortGroupList::clear_list ()
369 {
370         clear ();
371
372         _buss.clear ();
373         _track.clear ();
374         _system.clear ();
375         _other.clear ();
376
377         for (std::vector<sigc::connection>::iterator i = _visibility_connections.begin(); i != _visibility_connections.end(); ++i) {
378                 i->disconnect ();
379         }
380
381         _visibility_connections.clear ();
382         _bundles_dirty = true;
383 }
384
385 ARDOUR::BundleList const &
386 PortGroupList::bundles () const
387 {
388         if (_bundles_dirty) {
389                 update_bundles ();
390         }
391
392         return _bundles;
393 }