new transport slave/master implementation, libs/ edition
[ardour.git] / libs / ardour / transport_master.cc
1 /*
2     Copyright (C) 2002 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 <vector>
21
22 #include "pbd/i18n.h"
23
24 #include "ardour/audioengine.h"
25 #include "ardour/midi_port.h"
26 #include "ardour/session.h"
27 #include "ardour/transport_master.h"
28 #include "ardour/transport_master_manager.h"
29 #include "ardour/utils.h"
30
31 using namespace ARDOUR;
32
33 const std::string TransportMaster::state_node_name = X_("TransportMaster");
34
35
36 TransportMaster::TransportMaster (SyncSource t, std::string const & name)
37         : _type (t)
38         , _name (name)
39         , _session (0)
40         , _connected (false)
41         , _current_delta (0)
42         , _collect (true)
43         , _pending_collect (true)
44         , _request_mask (TransportRequestType (0))
45         , _sclock_synced (false)
46 {
47         ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect_same_thread (port_connection, boost::bind (&TransportMaster::connection_handler, this, _1, _2, _3, _4, _5));
48         ARDOUR::AudioEngine::instance()->Running.connect_same_thread (backend_connection, boost::bind (&TransportMaster::check_backend, this));
49 }
50
51 TransportMaster::~TransportMaster()
52 {
53         delete _session;
54 }
55
56 bool
57 TransportMaster::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
58 {
59         if (!_port) {
60                 return false;
61         }
62
63         const std::string fqn = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->name());
64
65         if (fqn == name1 || fqn == name2) {
66
67                 /* it's about us */
68
69                 if (yn) {
70                         _connected = true;
71                 } else {
72                         _connected = false;
73                 }
74
75                 return true;
76         }
77
78         return false;
79 }
80
81 bool
82 TransportMaster::check_collect()
83 {
84         if (!_connected) {
85                 return false;
86         }
87
88         /* XXX should probably use boost::atomic something or other here */
89
90         if (_pending_collect != _collect) {
91                 if (_pending_collect) {
92                         init ();
93                 } else {
94                         if (TransportMasterManager::instance().current().get() == this) {
95                                 if (_session) {
96                                         _session->config.set_external_sync (false);
97                                 }
98                         }
99                 }
100                 std::cerr << name() << " pc = " << _pending_collect << " c = " << _collect << std::endl;
101                 _collect = _pending_collect;
102         }
103
104         return _collect;
105 }
106
107 void
108 TransportMaster::set_collect (bool yn)
109 {
110         _pending_collect = yn;
111 }
112
113 void
114 TransportMaster::set_sample_clock_synced (bool yn)
115 {
116         _sclock_synced = yn;
117 }
118
119 void
120 TransportMaster::set_session (Session* s)
121 {
122         _session = s;
123 }
124
125 int
126 TransportMaster::set_state (XMLNode const & node, int /* version */)
127 {
128         if (!node.get_property (X_("collect"), _collect)) {
129                 _collect = false;
130         }
131
132         if (!node.get_property (X_("clock-synced"), _sclock_synced)) {
133                 _sclock_synced = false;
134         }
135
136         TimecodeTransportMaster* ttm = dynamic_cast<TimecodeTransportMaster*> (this);
137         if (ttm) {
138                 bool val;
139                 node.get_property (X_("fr2997"), val);
140                 ttm->set_fr2997 (val);
141         }
142
143         XMLNode* pnode = node.child (X_("Port"));
144
145         if (pnode) {
146                 XMLNodeList const & children = pnode->children();
147                 for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
148
149                         XMLProperty const *prop;
150
151                         if ((*ci)->name() == X_("Connection")) {
152                                 if ((prop = (*ci)->property (X_("other"))) == 0) {
153                                         continue;
154                                 }
155                                 _port->connect (prop->value());
156                         }
157                 }
158         }
159
160         return 0;
161 }
162
163 XMLNode&
164 TransportMaster::get_state ()
165 {
166         XMLNode* node = new XMLNode (state_node_name);
167         node->set_property (X_("type"), _type);
168         node->set_property (X_("name"), _name);
169         node->set_property (X_("collect"), _collect);
170         node->set_property (X_("clock-synced"), _sclock_synced);
171
172         TimecodeTransportMaster* ttm = dynamic_cast<TimecodeTransportMaster*> (this);
173         if (ttm) {
174                 node->set_property (X_("fr2997"), ttm->fr2997());
175         }
176
177         if (_port) {
178                 std::vector<std::string> connections;
179
180                 XMLNode* pnode = new XMLNode (X_("Port"));
181
182                 if (_port->get_connections (connections)) {
183
184                         std::vector<std::string>::const_iterator ci;
185                         std::sort (connections.begin(), connections.end());
186
187                         for (ci = connections.begin(); ci != connections.end(); ++ci) {
188
189                                 /* if its a connection to our own port,
190                                    return only the port name, not the
191                                    whole thing. this allows connections
192                                    to be re-established even when our
193                                    client name is different.
194                                 */
195
196                                 XMLNode* cnode = new XMLNode (X_("Connection"));
197
198                                 cnode->set_property (X_("other"), AudioEngine::instance()->make_port_name_relative (*ci));
199                                 pnode->add_child_nocopy (*cnode);
200                         }
201                 }
202
203                 node->add_child_nocopy (*pnode);
204         }
205
206         return *node;
207 }
208
209 boost::shared_ptr<TransportMaster>
210 TransportMaster::factory (XMLNode const & node)
211 {
212         if (node.name() != TransportMaster::state_node_name) {
213                 return boost::shared_ptr<TransportMaster>();
214         }
215
216         SyncSource type;
217         std::string name;
218
219         if (!node.get_property (X_("type"), type)) {
220                 return boost::shared_ptr<TransportMaster>();
221         }
222
223         if (!node.get_property (X_("name"), name)) {
224                 return boost::shared_ptr<TransportMaster>();
225         }
226
227         return factory (type, name);
228 }
229
230 boost::shared_ptr<TransportMaster>
231 TransportMaster::factory (SyncSource type, std::string const& name)
232 {
233         /* XXX need to count existing sources of a given type */
234
235         switch (type) {
236         case MTC:
237                 return boost::shared_ptr<TransportMaster> (new MTC_TransportMaster (sync_source_to_string (type)));
238         case LTC:
239                 return boost::shared_ptr<TransportMaster> (new LTC_TransportMaster (sync_source_to_string (type)));
240         case MIDIClock:
241                 return boost::shared_ptr<TransportMaster> (new MIDIClock_TransportMaster (sync_source_to_string (type)));
242         case Engine:
243                 return boost::shared_ptr<TransportMaster> (new Engine_TransportMaster (*AudioEngine::instance()));
244         default:
245                 break;
246         }
247
248         return boost::shared_ptr<TransportMaster>();
249 }
250
251 boost::shared_ptr<Port>
252 TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
253 {
254         boost::shared_ptr<Port> p;
255
256         if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name)) == 0) {
257                 return boost::shared_ptr<Port> ();
258         }
259
260         _midi_port = boost::dynamic_pointer_cast<MidiPort> (p);
261
262         return p;
263 }
264
265 bool
266 TransportMaster::allow_request (TransportRequestSource src, TransportRequestType type) const
267 {
268         return _request_mask & type;
269 }
270
271 void
272 TransportMaster::set_request_mask (TransportRequestType t)
273 {
274         _request_mask = t;
275 }
276
277 void
278 TimecodeTransportMaster::set_fr2997 (bool yn)
279 {
280         _fr2997 = yn;
281 }