f3aad266fe46116bb72798a8f10c162de86805a3
[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/debug.h"
23 #include "pbd/i18n.h"
24
25 #include "ardour/audioengine.h"
26 #include "ardour/midi_port.h"
27 #include "ardour/session.h"
28 #include "ardour/transport_master.h"
29 #include "ardour/transport_master_manager.h"
30 #include "ardour/types_convert.h"
31 #include "ardour/utils.h"
32
33 namespace ARDOUR {
34         namespace Properties {
35                 PBD::PropertyDescriptor<bool> fr2997;
36                 PBD::PropertyDescriptor<bool> sclock_synced;
37                 PBD::PropertyDescriptor<bool> collect;
38                 PBD::PropertyDescriptor<bool> connected;
39                 PBD::PropertyDescriptor<TransportRequestType> allowed_transport_requests;
40         }
41 }
42
43 using namespace ARDOUR;
44 using namespace PBD;
45
46 void
47 TransportMaster::make_property_quarks ()
48 {
49         Properties::fr2997.property_id = g_quark_from_static_string (X_("fr2997"));
50         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fr2997 = %1\n", Properties::fr2997.property_id));
51         Properties::sclock_synced.property_id = g_quark_from_static_string (X_("sclock_synced"));
52         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sclock_synced = %1\n", Properties::sclock_synced.property_id));
53         Properties::collect.property_id = g_quark_from_static_string (X_("collect"));
54         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for collect = %1\n", Properties::collect.property_id));
55         Properties::connected.property_id = g_quark_from_static_string (X_("connected"));
56         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for connected = %1\n", Properties::connected.property_id));
57         Properties::allowed_transport_requests.property_id = g_quark_from_static_string (X_("allowed_transport_requests"));
58         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for allowed_transport_requests = %1\n", Properties::allowed_transport_requests.property_id));
59 }
60
61 const std::string TransportMaster::state_node_name = X_("TransportMaster");
62
63 TransportMaster::TransportMaster (SyncSource t, std::string const & name)
64         : _type (t)
65         , _name (Properties::name, name)
66         , _session (0)
67         , _current_delta (0)
68         , _pending_collect (true)
69         , _request_mask (Properties::allowed_transport_requests, TransportRequestType (0))
70         , _locked (Properties::locked, false)
71         , _sclock_synced (Properties::sclock_synced, false)
72         , _collect (Properties::collect, true)
73         , _connected (Properties::connected, false)
74 {
75         register_properties ();
76
77         ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect_same_thread (port_connection, boost::bind (&TransportMaster::connection_handler, this, _1, _2, _3, _4, _5));
78         ARDOUR::AudioEngine::instance()->Running.connect_same_thread (backend_connection, boost::bind (&TransportMaster::check_backend, this));
79 }
80
81 TransportMaster::~TransportMaster()
82 {
83         delete _session;
84 }
85
86 void
87 TransportMaster::register_properties ()
88 {
89         _xml_node_name = state_node_name;
90
91         add_property (_name);
92         add_property (_locked);
93         add_property (_collect);
94         add_property (_sclock_synced);
95         add_property (_request_mask);
96
97         /* we omit _connected since it is derived from port state, and merely
98          * used for signalling
99          */
100 }
101
102 void
103 TransportMaster::set_name (std::string const & str)
104 {
105         if (_name != str) {
106                 _name = str;
107                 PropertyChange (Properties::name);
108         }
109 }
110
111 bool
112 TransportMaster::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
113 {
114         if (!_port) {
115                 return false;
116         }
117
118         const std::string fqn = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->name());
119
120         if (fqn == name1 || fqn == name2) {
121
122                 /* it's about us */
123
124                 /* XXX technically .. if the user makes an N->1 connection to
125                  * this transport master's port, this simple minded logic is
126                  * not sufficient. But the user shouldn't do that ...
127                  */
128
129                 if (yn) {
130                         _connected = true;
131                 } else {
132                         _connected = false;
133                 }
134
135                 PropertyChanged (Properties::connected);
136
137                 return true;
138         }
139
140         return false;
141 }
142
143 bool
144 TransportMaster::check_collect()
145 {
146         if (!_connected) {
147                 return false;
148         }
149
150         /* XXX should probably use boost::atomic something or other here */
151
152         if (_pending_collect != _collect) {
153                 if (_pending_collect) {
154                         init ();
155                 } else {
156                         if (TransportMasterManager::instance().current().get() == this) {
157                                 if (_session) {
158                                         _session->config.set_external_sync (false);
159                                 }
160                         }
161                 }
162                 _collect = _pending_collect;
163                 PropertyChanged (Properties::collect);
164         }
165
166         return _collect;
167 }
168
169 void
170 TransportMaster::set_collect (bool yn)
171 {
172         _pending_collect = yn;
173 }
174
175 void
176 TransportMaster::set_sample_clock_synced (bool yn)
177 {
178         if (yn != _sclock_synced) {
179                 _sclock_synced = yn;
180                 PropertyChanged (Properties::sclock_synced);
181         }
182 }
183
184 void
185 TransportMaster::set_session (Session* s)
186 {
187         _session = s;
188 }
189
190 int
191 TransportMaster::set_state (XMLNode const & node, int /* version */)
192 {
193         PropertyChange what_changed;
194
195         what_changed = set_values (node);
196
197         XMLNode* pnode = node.child (X_("Port"));
198
199         if (pnode) {
200                 XMLNodeList const & children = pnode->children();
201                 for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
202
203                         XMLProperty const *prop;
204
205                         if ((*ci)->name() == X_("Connection")) {
206                                 if ((prop = (*ci)->property (X_("other"))) == 0) {
207                                         continue;
208                                 }
209                                 _port->connect (prop->value());
210                         }
211                 }
212         }
213
214         PropertyChanged (what_changed);
215
216         return 0;
217 }
218
219 XMLNode&
220 TransportMaster::get_state ()
221 {
222         XMLNode* node = new XMLNode (state_node_name);
223         node->set_property (X_("type"), _type);
224
225         add_properties (*node);
226
227         if (_port) {
228                 std::vector<std::string> connections;
229
230                 XMLNode* pnode = new XMLNode (X_("Port"));
231
232                 if (_port->get_connections (connections)) {
233
234                         std::vector<std::string>::const_iterator ci;
235                         std::sort (connections.begin(), connections.end());
236
237                         for (ci = connections.begin(); ci != connections.end(); ++ci) {
238
239                                 /* if its a connection to our own port,
240                                    return only the port name, not the
241                                    whole thing. this allows connections
242                                    to be re-established even when our
243                                    client name is different.
244                                 */
245
246                                 XMLNode* cnode = new XMLNode (X_("Connection"));
247
248                                 cnode->set_property (X_("other"), AudioEngine::instance()->make_port_name_relative (*ci));
249                                 pnode->add_child_nocopy (*cnode);
250                         }
251                 }
252
253                 node->add_child_nocopy (*pnode);
254         }
255
256         return *node;
257 }
258
259 boost::shared_ptr<TransportMaster>
260 TransportMaster::factory (XMLNode const & node)
261 {
262         if (node.name() != TransportMaster::state_node_name) {
263                 return boost::shared_ptr<TransportMaster>();
264         }
265
266         SyncSource type;
267         std::string name;
268
269         if (!node.get_property (X_("type"), type)) {
270                 return boost::shared_ptr<TransportMaster>();
271         }
272
273         if (!node.get_property (X_("name"), name)) {
274                 return boost::shared_ptr<TransportMaster>();
275         }
276
277         return factory (type, name);
278 }
279
280 boost::shared_ptr<TransportMaster>
281 TransportMaster::factory (SyncSource type, std::string const& name)
282 {
283         /* XXX need to count existing sources of a given type */
284
285         switch (type) {
286         case MTC:
287                 return boost::shared_ptr<TransportMaster> (new MTC_TransportMaster (sync_source_to_string (type)));
288         case LTC:
289                 return boost::shared_ptr<TransportMaster> (new LTC_TransportMaster (sync_source_to_string (type)));
290         case MIDIClock:
291                 return boost::shared_ptr<TransportMaster> (new MIDIClock_TransportMaster (sync_source_to_string (type)));
292         case Engine:
293                 return boost::shared_ptr<TransportMaster> (new Engine_TransportMaster (*AudioEngine::instance()));
294         default:
295                 break;
296         }
297
298         return boost::shared_ptr<TransportMaster>();
299 }
300
301 boost::shared_ptr<Port>
302 TransportMasterViaMIDI::create_midi_port (std::string const & port_name)
303 {
304         boost::shared_ptr<Port> p;
305
306         if ((p = AudioEngine::instance()->register_input_port (DataType::MIDI, port_name)) == 0) {
307                 return boost::shared_ptr<Port> ();
308         }
309
310         _midi_port = boost::dynamic_pointer_cast<MidiPort> (p);
311
312         return p;
313 }
314
315 bool
316 TransportMaster::allow_request (TransportRequestSource src, TransportRequestType type) const
317 {
318         return _request_mask & type;
319 }
320
321 void
322 TransportMaster::set_request_mask (TransportRequestType t)
323 {
324         if (_request_mask != t) {
325                 _request_mask = t;
326                 PropertyChanged (Properties::allowed_transport_requests);
327         }
328 }
329
330 TimecodeTransportMaster::TimecodeTransportMaster (std::string const & name, SyncSource type)
331         : TransportMaster (type, name)
332         , _fr2997 (Properties::fr2997, false)
333 {
334         register_properties ();
335 }
336
337 void
338 TimecodeTransportMaster::register_properties ()
339 {
340         TransportMaster::register_properties ();
341         add_property (_fr2997);
342 }
343
344 void
345 TimecodeTransportMaster::set_fr2997 (bool yn)
346 {
347         if (yn != _fr2997) {
348                 _fr2997 = yn;
349                 PropertyChanged (Properties::fr2997);
350         }
351 }