1 /*
2  * Copyright (C) 2018-2019 Paul Davis <paul@linuxaudiosystems.com>
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 along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include "pbd/stateful.h"
20 
21 #include "ardour/audioengine.h"
22 #include "ardour/boost_debug.h"
23 #include "ardour/debug.h"
24 #include "ardour/disk_reader.h"
25 #include "ardour/session.h"
26 #include "ardour/rc_configuration.h"
27 #include "ardour/transport_master_manager.h"
28 
29 #include "pbd/i18n.h"
30 
31 using namespace std;
32 using namespace ARDOUR;
33 using namespace PBD;
34 
35 const std::string TransportMasterManager::state_node_name = X_("TransportMasters");
36 TransportMasterManager* TransportMasterManager::_instance = 0;
37 
TransportMasterManager()38 TransportMasterManager::TransportMasterManager()
39 	: _master_speed (0)
40 	, _master_position (0)
41 	, _session (0)
42 	, _master_invalid_this_cycle (false)
43 	, disk_output_blocked (false)
44 	, master_dll_initstate (0)
45 {
46 }
47 
~TransportMasterManager()48 TransportMasterManager::~TransportMasterManager ()
49 {
50 	Glib::Threads::RWLock::WriterLock lm (lock);
51 	_current_master.reset ();
52 	_transport_masters.clear ();
53 }
54 
55 TransportMasterManager&
create()56 TransportMasterManager::create ()
57 {
58 	assert (!_instance);
59 
60 	_instance = new TransportMasterManager;
61 
62 	XMLNode* tmm_node = Config->transport_master_state ();
63 
64 	if (tmm_node) {
65 		_instance->set_state (*tmm_node, Stateful::current_state_version);
66 	} else {
67 		_instance->set_default_configuration ();
68 	}
69 
70 	return *_instance;
71 }
72 
73 int
set_default_configuration()74 TransportMasterManager::set_default_configuration ()
75 {
76 	try {
77 
78 		clear ();
79 
80 		/* setup default transport masters. Most people will never need any
81 		   others
82 		*/
83 
84 		add (Engine, X_("JACK Transport"), false);
85 		add (MTC, X_("MTC"), false);
86 		add (LTC, X_("LTC"), false);
87 		add (MIDIClock, X_("MIDI Clock"), false);
88 
89 	} catch (...) {
90 		return -1;
91 	}
92 
93 	_current_master = _transport_masters.back();
94 	return 0;
95 }
96 
97 void
set_session(Session * s)98 TransportMasterManager::set_session (Session* s)
99 {
100 	/* Called by AudioEngine in process context, synchronously with it's
101 	 * own "adoption" of the Session. The call will occur before the first
102 	 * call to ::pre_process_transport_masters().
103 	 */
104 
105 	Glib::Threads::RWLock::ReaderLock lm (lock);
106 
107 	maybe_restore_tc_format ();
108 
109 	config_connection.disconnect ();
110 
111 	_session = s;
112 
113 	for (TransportMasters::iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
114 		(*tm)->set_session (s);
115 	}
116 
117 	if (_session) {
118 		_session->config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&TransportMasterManager::parameter_changed, this, _1));
119 	}
120 
121 }
122 
123 void
parameter_changed(std::string const & what)124 TransportMasterManager::parameter_changed (std::string const & what)
125 {
126 	if (what == "external-sync") {
127 		if (!_session->config.get_external_sync()) {
128 			/* disabled */
129 			unblock_disk_output ();
130 		}
131 	}
132 }
133 
134 TransportMasterManager&
instance()135 TransportMasterManager::instance()
136 {
137 	if (!_instance) {
138 		fatal << string_compose (_("programming error:%1"), X_("TransportMasterManager::instance() called without an instance!")) << endmsg;
139 		abort (); /* NOTREACHED */
140 	}
141 	return *_instance;
142 }
143 
144 void
destroy()145 TransportMasterManager::destroy()
146 {
147 	delete _instance;
148 	_instance = 0;
149 }
150 
151 // Called from AudioEngine::process_callback() BEFORE Session::process() is called. Each transport master has processed any incoming data for this cycle,
152 // and this method computes the transport speed that Ardour should use to get into and remain in sync with the master.
153 //
154 double
pre_process_transport_masters(pframes_t nframes,samplepos_t now)155 TransportMasterManager::pre_process_transport_masters (pframes_t nframes, samplepos_t now)
156 {
157 	Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
158 
159 	if (!lm.locked()) {
160 		return 1.0;
161 	}
162 
163 	boost::optional<samplepos_t> session_pos;
164 
165 	if (_session) {
166 		session_pos = _session->audible_sample();
167 	}
168 
169 	if (Config->get_run_all_transport_masters_always()) {
170 		for (TransportMasters::iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
171 			if ((*tm)->check_collect()) {
172 				(*tm)->pre_process (nframes, now, session_pos);
173 			}
174 		}
175 	}
176 
177 	if (!_session) {
178 		return 1.0;
179 	}
180 
181 	/* if we're not running ALL transport masters, but still have a current
182 	 * one, then we should run that one all the time so that we know
183 	 * precisely where it is when we starting chasing it ...
184 	 */
185 
186 	if (!Config->get_run_all_transport_masters_always() && _current_master) {
187 		_current_master->pre_process (nframes, now, session_pos);
188 	}
189 
190 	if (!_session->config.get_external_sync()) {
191 		maybe_restore_tc_format ();
192 		DEBUG_TRACE (DEBUG::Slave, string_compose ("no external sync, use session actual speed of %1\n", _session->actual_speed() ? _session->actual_speed() : 1.0));
193 		return _session->actual_speed () ? _session->actual_speed() : 1.0;
194 	}
195 
196 	/* --- NOT REACHED UNLESS CHASING (i.e. _session->config.get_external_sync() is true ------*/
197 
198 	if (!_current_master->ok()) {
199 		/* stop */
200 		_session->request_stop (false, false, _current_master->request_type());
201 		DEBUG_TRACE (DEBUG::Slave, "no roll2 - master has failed\n");
202 		_master_invalid_this_cycle = true;
203 		return 1.0;
204 	}
205 
206 	if (!_current_master->locked()) {
207 		DEBUG_TRACE (DEBUG::Slave, "no roll4 - not locked\n");
208 		_master_invalid_this_cycle = true;
209 		return 1.0;
210 	}
211 
212 	double engine_speed;
213 	samplepos_t ignore1, ignore2;
214 
215 	if (!_current_master->speed_and_position (_master_speed, _master_position, ignore1, ignore2, now)) {
216 		return 1.0;
217 	}
218 
219 	DEBUG_TRACE (DEBUG::Slave, string_compose ("Current master at %1 moving at %2\n", _master_position, _master_speed));
220 
221 	if (_current_master->sample_clock_synced()) {
222 
223 		/* No master DLL required. Speed identified by the master is
224 		 * our speed, quantized to {1.0, 0.0, -1.0}
225 		 */
226 
227 		if (_master_speed > 0.0f) {
228 			engine_speed = 1.0f;
229 		} else if (_master_speed < 0.0f) {
230 			engine_speed = -1.0f;
231 		} else {
232 			engine_speed = 0.0f;
233 		}
234 
235 		DEBUG_TRACE (DEBUG::Slave, string_compose ("S-clock synced master speed %1 used as %2\n", _master_speed, engine_speed));
236 
237 	} else if (_master_speed != 0.0) {
238 
239 		samplepos_t delta = _master_position;
240 
241 		if (_session->compute_audible_delta (delta)) {
242 
243 			if (master_dll_initstate == 0) {
244 
245 				init_transport_master_dll (_master_speed, _master_position);
246 				DEBUG_TRACE (DEBUG::Slave, string_compose ("initializing master DLL, will be %1 next process cycle\n", master_dll_initstate));
247 
248 				return _master_speed;
249 			}
250 
251 			/* compute delta or "error" between the computed master_position for
252 			 * this cycle and the current session position.
253 			 *
254 			 * Remember: ::speed_and_position() is being called in process context
255 			 * but returns the predicted speed+position for the start of this process cycle,
256 			 * not just the most recent timestamp received by the current master object.
257 			 */
258 
259 			DEBUG_TRACE (DEBUG::Slave, string_compose ("master DLL: delta = %1 (%2 vs %3) res: %4\n", delta, _master_position, _session->transport_sample(), _current_master->resolution()));
260 
261 			if (delta > _current_master->resolution()) {
262 
263 				// init_transport_master_dll (_master_speed, _master_position);
264 
265 				if (!_session->actively_recording()) {
266 					DEBUG_TRACE (DEBUG::Slave, string_compose ("slave delta %1 greater than slave resolution %2 => no disk output\n", delta, _current_master->resolution()));
267 					/* run routes as normal, but no disk output */
268 					block_disk_output ();
269 				} else {
270 					unblock_disk_output ();
271 				}
272 			} else {
273 				unblock_disk_output ();
274 			}
275 
276 			/* inject DLL with new data */
277 
278 			DEBUG_TRACE (DEBUG::Slave, string_compose ("feed master DLL t0 %1 t1 %2 e %3 %4 e2 %5 sess %6\n", t0, t1, delta, _master_position, e2, _session->transport_sample()));
279 
280 			const double e = delta;
281 
282 			t0 = t1;
283 			t1 += b * e + e2;
284 			e2 += c * e;
285 
286 			engine_speed = (t1 - t0) / nframes;
287 
288 			DEBUG_TRACE (DEBUG::Slave, string_compose ("slave @ %1 speed %2 cur delta %3 matching speed %4\n", _master_position, _master_speed, delta, engine_speed));
289 
290 			/* provide a .1% deadzone to lock the speed */
291 			if (fabs (engine_speed - 1.0) <= 0.001) {
292 				engine_speed = 1.0;
293 			}
294 
295 			/* speed is set, we're locked, and good to go */
296 			DEBUG_TRACE (DEBUG::Slave, string_compose ("%1: computed speed-to-follow-master as %2\n", _current_master->name(), engine_speed));
297 
298 		} else {
299 
300 			/* session has not finished with latency compensation yet, so we cannot compute the
301 			   difference between the master and the session.
302 			*/
303 			engine_speed = 1.0;
304 		}
305 
306 	} else {
307 
308 		engine_speed = 1.0;
309 	}
310 
311 	_master_invalid_this_cycle = false;
312 
313 	maybe_set_tc_format ();
314 
315 	DEBUG_TRACE (DEBUG::Slave, string_compose ("computed resampling ratio as %1 with position = %2 and speed = %3\n", engine_speed, _master_position, _master_speed));
316 
317 	return engine_speed;
318 }
319 
320 void
maybe_restore_tc_format()321 TransportMasterManager::maybe_restore_tc_format ()
322 {
323 	if (_session && _session_tc_format) {
324 		_session->config.set_timecode_format (*_session_tc_format);
325 	}
326 	_session_tc_format.reset ();
327 }
328 
329 void
maybe_set_tc_format()330 TransportMasterManager::maybe_set_tc_format ()
331 {
332 	if (!Config->get_timecode_sync_frame_rate() || !_session) {
333 		return;
334 	}
335 	boost::shared_ptr<TimecodeTransportMaster> tcm;
336 	if ((tcm = boost::dynamic_pointer_cast<TimecodeTransportMaster>(_current_master)) == 0) {
337 		return;
338 	}
339 
340 	if (!tcm->apparent_timecode_format_valid ()) {
341 		return;
342 	}
343 
344 	Timecode::TimecodeFormat stf = _session->config.get_timecode_format();
345 	Timecode::TimecodeFormat mtf = tcm->apparent_timecode_format ();
346 
347 	if (stf == mtf) {
348 		return;
349 	}
350 
351 	/* save session's original TC */
352 	if (!_session_tc_format) {
353 		_session_tc_format = stf;
354 	}
355 
356 	warning << string_compose(_("Transport master adjusted framerate from %1 to %2."),
357 			Timecode::timecode_format_name(stf),
358 			Timecode::timecode_format_name(mtf))
359 		<< endmsg;
360 
361 	_session->config.set_timecode_format (mtf);
362 }
363 
364 void
init_transport_master_dll(double speed,samplepos_t pos)365 TransportMasterManager::init_transport_master_dll (double speed, samplepos_t pos)
366 {
367 	/* the bandwidth of the DLL is a trade-off,
368 	 * because the max-speed of the transport in ardour is
369 	 * limited to +-8.0, a larger bandwidth would cause oscillations
370 	 *
371 	 * But this is only really a problem if the user performs manual
372 	 * seeks while transport is running and slaved to some timecode-y master.
373 	 */
374 
375 	AudioEngine* ae = AudioEngine::instance();
376 
377 	double const omega = 2.0 * M_PI * double(ae->samples_per_cycle()) / 2.0 / double(ae->sample_rate());
378 	b = 1.4142135623730950488 * omega;
379 	c = omega * omega;
380 
381 	const int direction = (speed >= 0.0 ? 1 : -1);
382 
383 	master_dll_initstate = direction;
384 
385 	e2 = double (direction * ae->samples_per_cycle());
386 	t0 = double (pos);
387 	t1 = t0 + e2;
388 
389 	DEBUG_TRACE (DEBUG::Slave, string_compose ("[re-]init ENGINE DLL %1 %2 %3 from %4 %5\n", t0,  t1, e2, speed, pos));
390 }
391 
392 int
add(SyncSource type,std::string const & name,bool removeable)393 TransportMasterManager::add (SyncSource type, std::string const & name, bool removeable)
394 {
395 	int ret = 0;
396 	boost::shared_ptr<TransportMaster> tm;
397 
398 	DEBUG_TRACE (DEBUG::Slave, string_compose ("adding new transport master, type %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
399 
400 	{
401 		Glib::Threads::RWLock::WriterLock lm (lock);
402 
403 		for (TransportMasters::const_iterator t = _transport_masters.begin(); t != _transport_masters.end(); ++t) {
404 			if ((*t)->name() == name) {
405 				error << string_compose (_("There is already a transport master named \"%1\" - not duplicated"), name) << endmsg;
406 				return -1;
407 			}
408 		}
409 
410 		tm = TransportMaster::factory (type, name, removeable);
411 
412 		if (!tm) {
413 			return -1;
414 		}
415 
416 		BOOST_MARK_TMM (tm);
417 
418 		ret = add_locked (tm);
419 	}
420 
421 	if (ret == 0) {
422 		Added (tm);
423 	}
424 
425 	return ret;
426 }
427 
428 int
add_locked(boost::shared_ptr<TransportMaster> tm)429 TransportMasterManager::add_locked (boost::shared_ptr<TransportMaster> tm)
430 {
431 	if (!tm) {
432 		return -1;
433 	}
434 
435 
436 	if (_session) {
437 		tm->set_session (_session);
438 	}
439 
440 	_transport_masters.push_back (tm);
441 	return 0;
442 }
443 
444 int
remove(std::string const & name)445 TransportMasterManager::remove (std::string const & name)
446 {
447 	int ret = -1;
448 	boost::shared_ptr<TransportMaster> tm;
449 
450 	{
451 		Glib::Threads::RWLock::WriterLock lm (lock);
452 
453 		for (TransportMasters::iterator t = _transport_masters.begin(); t != _transport_masters.end(); ++t) {
454 			if ((*t)->name() == name) {
455 				if (!(*t)->removeable()) {
456 					return -1;
457 				}
458 				tm = *t;
459 				_transport_masters.erase (t);
460 				ret = 0;
461 				break;
462 			}
463 		}
464 	}
465 
466 	if (ret == 0) {
467 		Removed (tm);
468 	}
469 
470 	return ret;
471 }
472 
473 int
set_current_locked(boost::shared_ptr<TransportMaster> c)474 TransportMasterManager::set_current_locked (boost::shared_ptr<TransportMaster> c)
475 {
476 	if (c) {
477 		if (find (_transport_masters.begin(), _transport_masters.end(), c) == _transport_masters.end()) {
478 			warning << string_compose (X_("programming error: attempt to use unknown transport master \"%1\"\n"), c->name());
479 			return -1;
480 		}
481 	}
482 
483 	maybe_restore_tc_format ();
484 
485 	if (!c->usable()) {
486 		return -1;
487 	}
488 
489 	/* this is called from within the process() call stack, but *after* the
490 	 * call to ::pre_process_transport_masters()
491 	 */
492 
493 	_current_master = c;
494 	_master_speed = 0;
495 	_master_position = 0;
496 	_master_invalid_this_cycle = true;
497 
498 	master_dll_initstate = 0;
499 
500 	unblock_disk_output ();
501 
502 	if (c && c->type() == Engine) {
503 
504 		/* We cannot sync with an already moving JACK transport mechanism, so
505 		 * stop it before we start.
506 		 */
507 
508 		AudioEngine::instance()->transport_stop ();
509 	}
510 
511 	DEBUG_TRACE (DEBUG::Slave, string_compose ("current transport master set to %1\n", (c ? c->name() : string ("none"))));
512 
513 	return 0;
514 }
515 
516 int
set_current(boost::shared_ptr<TransportMaster> c)517 TransportMasterManager::set_current (boost::shared_ptr<TransportMaster> c)
518 {
519 	int ret = -1;
520 	boost::shared_ptr<TransportMaster> old (_current_master);
521 
522 	{
523 		Glib::Threads::RWLock::WriterLock lm (lock);
524 		ret = set_current_locked (c);
525 	}
526 
527 	if (ret == 0) {
528 		CurrentChanged (old, _current_master); // EMIT SIGNAL
529 	}
530 
531 	return ret;
532 }
533 
534 int
set_current(SyncSource ss)535 TransportMasterManager::set_current (SyncSource ss)
536 {
537 	int ret = -1;
538 	boost::shared_ptr<TransportMaster> old (_current_master);
539 
540 	{
541 		Glib::Threads::RWLock::WriterLock lm (lock);
542 
543 		for (TransportMasters::iterator t = _transport_masters.begin(); t != _transport_masters.end(); ++t) {
544 			if ((*t)->type() == ss) {
545 				ret = set_current_locked (*t);
546 				break;
547 			}
548 		}
549 	}
550 
551 	if (ret == 0) {
552 		CurrentChanged (old, _current_master); // EMIT SIGNAL
553 	}
554 
555 	return ret;
556 }
557 
558 
559 int
set_current(std::string const & str)560 TransportMasterManager::set_current (std::string const & str)
561 {
562 	int ret = -1;
563 	boost::shared_ptr<TransportMaster> old (_current_master);
564 
565 	{
566 		Glib::Threads::RWLock::WriterLock lm (lock);
567 
568 		for (TransportMasters::iterator t = _transport_masters.begin(); t != _transport_masters.end(); ++t) {
569 			if ((*t)->name() == str) {
570 				ret = set_current_locked (*t);
571 				break;
572 			}
573 		}
574 	}
575 
576 	if (ret == 0) {
577 		CurrentChanged (old, _current_master); // EMIT SIGNAL
578 	}
579 
580 	return ret;
581 }
582 
583 
584 void
clear()585 TransportMasterManager::clear ()
586 {
587 	{
588 		Glib::Threads::RWLock::WriterLock lm (lock);
589 		_current_master.reset ();
590 		_transport_masters.clear ();
591 	}
592 
593 	Removed (boost::shared_ptr<TransportMaster>());
594 }
595 
596 int
set_state(XMLNode const & node,int version)597 TransportMasterManager::set_state (XMLNode const & node, int version)
598 {
599 	assert (node.name() == state_node_name);
600 
601 	XMLNodeList const & children = node.children();
602 
603 	{
604 		Glib::Threads::RWLock::WriterLock lm (lock);
605 
606 		_current_master.reset ();
607 #if 0
608 		boost_debug_list_ptrs ();
609 #endif
610 
611 		/* TramsportMasters live for the entire life of the
612 		 * program. TransportMasterManager::set_state() should only be
613 		 * called at the start of the program, and there should be no
614 		 * transport masters at that time.
615 		 */
616 
617 		assert (_transport_masters.empty());
618 
619 		for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
620 
621 			boost::shared_ptr<TransportMaster> tm = TransportMaster::factory (**c);
622 
623 			if (!tm) {
624 				continue;
625 			}
626 
627 			BOOST_MARK_TMM (tm);
628 
629 			if (add_locked (tm)) {
630 				continue;
631 			}
632 
633 			/* we know it is the last thing added to the list of masters */
634 
635 			_transport_masters.back()->set_state (**c, version);
636 		}
637 	}
638 
639 	/* fallback choice, lives on until ::restart() is called after the
640 	 * engine is running.
641 	 */
642 
643 	set_current (MTC);
644 
645 	return 0;
646 }
647 
648 XMLNode&
get_state()649 TransportMasterManager::get_state ()
650 {
651 	XMLNode* node = new XMLNode (state_node_name);
652 
653 	if (_current_master) {
654 		node->set_property (X_("current"), _current_master->name());
655 	}
656 
657 	Glib::Threads::RWLock::ReaderLock lm (lock);
658 
659 	for (TransportMasters::iterator t = _transport_masters.begin(); t != _transport_masters.end(); ++t) {
660 		node->add_child_nocopy ((*t)->get_state());
661 	}
662 
663 	return *node;
664 }
665 
666 boost::shared_ptr<TransportMaster>
master_by_type(SyncSource src) const667 TransportMasterManager::master_by_type (SyncSource src) const
668 {
669 	Glib::Threads::RWLock::ReaderLock lm (lock);
670 
671 	for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
672 		if ((*tm)->type() == src) {
673 			return *tm;
674 		}
675 	}
676 
677 	return boost::shared_ptr<TransportMaster> ();
678 }
679 
680 boost::shared_ptr<TransportMaster>
master_by_port(boost::shared_ptr<Port> const & p) const681 TransportMasterManager::master_by_port (boost::shared_ptr<Port> const &p) const
682 {
683 	Glib::Threads::RWLock::ReaderLock lm (lock);
684 
685 	for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
686 		if ((*tm)->port() == p) {
687 			return *tm;
688 		}
689 	}
690 
691 	return boost::shared_ptr<TransportMaster> ();
692 
693 }
694 
695 void
engine_stopped()696 TransportMasterManager::engine_stopped ()
697 {
698 	DEBUG_TRACE (DEBUG::Slave, "engine stopped, reset all transport masters\n");
699 	{
700 		Glib::Threads::RWLock::ReaderLock lm (lock);
701 
702 		for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
703 			(*tm)->reset (false);
704 		}
705 	}
706 }
707 
708 void
restart()709 TransportMasterManager::restart ()
710 {
711 	XMLNode* node;
712 
713 	if ((node = Config->transport_master_state()) != 0) {
714 
715 		{
716 			Glib::Threads::RWLock::ReaderLock lm (lock);
717 
718 			for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
719 				(*tm)->connect_port_using_state ();
720 				(*tm)->reset (false);
721 			}
722 		}
723 
724 		/* engine is running, connections are viable ... try to set current */
725 
726 		std::string current_master;
727 
728 		if (node->get_property (X_("current"), current_master)) {
729 
730 			/* may fal if current_master is not usable */
731 
732 			set_current (current_master);
733 		}
734 
735 	} else {
736 		if (TransportMasterManager::instance().set_default_configuration ()) {
737 			error << _("Cannot initialize transport master manager") << endmsg;
738 			/* XXX now what? */
739 		}
740 	}
741 }
742 
743 void
reconnect_ports()744 TransportMasterManager::reconnect_ports ()
745 {
746 	DEBUG_TRACE (DEBUG::Slave, "reconnecting all transport master ports\n");
747 	{
748 		Glib::Threads::RWLock::ReaderLock lm (lock);
749 
750 		for (TransportMasters::const_iterator tm = _transport_masters.begin(); tm != _transport_masters.end(); ++tm) {
751 			(*tm)->connect_port_using_state ();
752 		}
753 	}
754 }
755 
756 void
block_disk_output()757 TransportMasterManager::block_disk_output ()
758 {
759 	if (!disk_output_blocked) {
760 		//DiskReader::inc_no_disk_output ();
761 		disk_output_blocked = true;
762 	}
763 }
764 
765 void
unblock_disk_output()766 TransportMasterManager::unblock_disk_output ()
767 {
768 	if (disk_output_blocked) {
769 		//DiskReader::dec_no_disk_output ();
770 		disk_output_blocked = false;
771 	}
772 }
773 
774 void
reinit(double speed,samplepos_t pos)775 TransportMasterManager::reinit (double speed, samplepos_t pos)
776 {
777 	init_transport_master_dll (speed, pos);
778 }
779