*/
+#ifdef WAF_BUILD
+#include "libardour-config.h"
+#endif
+
#include <cmath>
#include <cerrno>
#include <unistd.h>
-
#include "pbd/undo.h"
#include "pbd/error.h"
#include "pbd/enumwriter.h"
#include "midi++/port.h"
#include "midi++/manager.h"
-#include "ardour/ardour.h"
#include "ardour/audioengine.h"
#include "ardour/auditioner.h"
#include "ardour/butler.h"
+#include "ardour/click.h"
#include "ardour/debug.h"
#include "ardour/location.h"
#include "ardour/session.h"
#include "ardour/slave.h"
+#include "ardour/operations.h"
#include "i18n.h"
queue_event (ev);
}
+/** Request a new transport speed, but if the speed parameter is exactly zero then use
+ * a very small +ve value to prevent the transport actually stopping. This method should
+ * be used by callers who are varying transport speed but don't ever want to stop it.
+ */
+void
+Session::request_transport_speed_nonzero (double speed)
+{
+ if (speed == 0) {
+ speed = DBL_EPSILON;
+ }
+
+ request_transport_speed (speed);
+}
+
void
Session::request_track_speed (Track* tr, double speed)
{
void
Session::realtime_stop (bool abort, bool clear_state)
{
- DEBUG_TRACE (DEBUG::Transport, "realtime stop\n");
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1\n", _transport_frame));
PostTransportWork todo = PostTransportWork (0);
/* assume that when we start, we'll be moving forwards */
past that point to pick up delayed input (and/or to delick)
*/
- if (_worst_output_latency > current_block_size) {
- /* we rolled past the stop point to pick up data that had
- not yet arrived. move back to where the stop occured.
- */
- decrement_transport_position (current_block_size + (_worst_output_latency - current_block_size));
- } else {
- decrement_transport_position (current_block_size);
- }
+ if (worst_playback_latency() > current_block_size) {
+ /* we rolled past the stop point to pick up data that had
+ not yet arrived. move back to where the stop occured.
+ */
+ decrement_transport_position (current_block_size + (worst_input_latency() - current_block_size));
+ } else {
+ decrement_transport_position (current_block_size);
+ }
/* the duration change is not guaranteed to have happened, but is likely */
_clear_event_type (SessionEvent::RangeStop);
_clear_event_type (SessionEvent::RangeLocate);
- /* if we're going to clear loop state, then force disabling record BUT only if we're not doing latched rec-enable */
+ /* if we're going to clear loop state, then force disabling record BUT only if we're not doing latched rec-enable */
disable_record (true, (!Config->get_latched_record_enable() && clear_state));
-
+
reset_slave_state ();
_transport_speed = 0;
transport_sub_state = 0;
}
+void
+Session::realtime_locate ()
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->realtime_locate ();
+ }
+}
+
void
Session::butler_transport_work ()
{
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1\n", enum_2_string (ptw)));
- if (ptw & PostTransportAdjustPlaybackBuffering) {
+ if (ptw & PostTransportAdjustPlaybackBuffering) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
tr->adjust_playback_buffering ();
- /* and refill those buffers ... */
- tr->non_realtime_locate (_transport_frame);
+ /* and refill those buffers ... */
+ tr->non_realtime_locate (_transport_frame);
}
}
-
- }
- if (ptw & PostTransportAdjustCaptureBuffering) {
+ }
+
+ if (ptw & PostTransportAdjustCaptureBuffering) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
tr->adjust_capture_buffering ();
}
}
- }
+ }
if (ptw & PostTransportCurveRealloc) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
tr->non_realtime_locate (_transport_frame);
}
}
+
+ /* XXX: it would be nice to generate the new clicks here (in the non-RT thread)
+ rather than clearing them so that the RT thread has to spend time constructing
+ them (in Session::click).
+ */
+ clear_clicks ();
}
auditioner->cancel_audition ();
}
- clear_clicks();
cumulative_rf_motion = 0;
reset_rf_scale (0);
if (did_record) {
- begin_reversible_command ("capture");
+ begin_reversible_command (Operations::capture);
_have_captured = true;
}
DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n"));
- if (abort && did_record) {
- /* no reason to save the session file when we remove sources
- */
- _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
- }
+ if (abort && did_record) {
+ /* no reason to save the session file when we remove sources
+ */
+ _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
+ }
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
}
}
- if (abort && did_record) {
- _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
- }
+ if (abort && did_record) {
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
+ }
boost::shared_ptr<RouteList> r = routes.reader ();
}
if (_engine.running()) {
- update_latency_compensation (true, abort);
+ PostTransportWork ptw = post_transport_work ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->nonrealtime_handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush));
+ }
+ update_latency_compensation ();
}
bool const auto_return_enabled =
if ((auto_return_enabled || synced_to_jack() || _requested_return_frame >= 0) &&
!(ptw & PostTransportLocate)) {
- /* no explicit locate queued */
+ /* no explicit locate queued */
bool do_locate = false;
if (_requested_return_frame >= 0) {
/* explicit return request pre-queued in event list. overrides everything else */
-
+
cerr << "explicit auto-return to " << _requested_return_frame << endl;
_transport_frame = _requested_return_frame;
if (!synced_to_jack()) {
Location *location = _locations->auto_loop_location();
-
+
if (location != 0) {
_transport_frame = location->start();
} else {
}
} else {
-
+
/* regular auto-return */
-
+
_transport_frame = _last_roll_location;
do_locate = true;
}
- }
+ }
}
- _requested_return_frame = -1;
+ _requested_return_frame = -1;
if (do_locate) {
_engine.transport_locate (_transport_frame);
}
- }
+ }
}
+ clear_clicks();
+
/* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode.
*/
have_looped = false;
- send_full_time_code (_transport_frame);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
- send_mmc_locate (_transport_frame);
+ if (!_engine.freewheeling()) {
+ send_full_time_code (_transport_frame);
+
+ if (!dynamic_cast<MTC_Slave*>(_slave)) {
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+ send_mmc_locate (_transport_frame);
+ }
+ }
if ((ptw & PostTransportLocate) && get_record_enabled()) {
/* capture start has been changed, so save pending state */
saved = true;
}
- /* always try to get rid of this */
+ /* always try to get rid of this */
- remove_pending_capture_state ();
+ remove_pending_capture_state ();
/* save the current state of things if appropriate */
{
play_loop = false;
clear_events (SessionEvent::AutoLoop);
-
+
// set all tracks to NOT use internal looping
boost::shared_ptr<RouteList> rl = routes.reader ();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
/* nothing to do, or can't change loop status while recording */
return;
}
-
+
if (yn && Config->get_seamless_loop() && synced_to_jack()) {
- warning << string_compose (_("Seamless looping cannot be supported while %1 is using JACK transport.\n"
- "Recommend changing the configured options"), PROGRAM_NAME)
+ warning << string_compose (
+ _("Seamless looping cannot be supported while %1 is using JACK transport.\n"
+ "Recommend changing the configured options"), PROGRAM_NAME)
<< endmsg;
return;
}
-
+
if (yn) {
play_loop = true;
}
}
}
-
+
/* put the loop event into the event list */
-
+
SessionEvent* event = new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f);
merge_event (event);
- /* locate to start of loop and roll. If doing seamless loop, force a
+ /* locate to start of loop and roll. If doing seamless loop, force a
locate+buffer refill even if we are positioned there already.
*/
return;
}
- // Update Timecode time
- // [DR] FIXME: find out exactly where this should go below
- _transport_frame = target_frame;
- timecode_time(_transport_frame, transmitting_timecode_time);
- outbound_mtc_timecode_frame = _transport_frame;
- next_quarter_frame_to_send = 0;
-
- if (_transport_speed && (!with_loop || loop_changing)) {
+ if (_transport_speed) {
/* schedule a declick. we'll be called again when its done */
if (!(transport_sub_state & PendingDeclickOut)) {
}
}
+ // Update Timecode time
+ // [DR] FIXME: find out exactly where this should go below
+ _transport_frame = target_frame;
+ timecode_time(_transport_frame, transmitting_timecode_time);
+ outbound_mtc_timecode_frame = _transport_frame;
+ next_quarter_frame_to_send = 0;
+
+ /* do "stopped" stuff if:
+ *
+ * we are rolling AND
+ * no autoplay in effect AND
+ * we're not going to keep rolling after the locate AND
+ * !(playing a loop with JACK sync)
+ *
+ */
+
if (transport_rolling() && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) {
realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
+ } else {
+ /* otherwise tell the world that we located */
+ realtime_locate ();
}
if (force || !with_loop || loop_changing) {
if (with_roll) {
todo = PostTransportWork (todo | PostTransportRoll);
- }
+ }
add_post_transport_work (todo);
_butler->schedule_transport_work ();
if (with_roll) {
/* switch from input if we're going to roll */
if (Config->get_monitoring_model() == HardwareMonitoring) {
-
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && tr->record_enabled ()) {
- //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
- tr->monitor_input (!config.get_auto_input());
- }
- }
+ set_track_monitor_input_status (!config.get_auto_input());
}
} else {
/* otherwise we're going to stop, so do the opposite */
if (Config->get_monitoring_model() == HardwareMonitoring) {
-
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && tr->record_enabled ()) {
- //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
- tr->monitor_input (true);
- }
- }
+ set_track_monitor_input_status (true);
}
}
}
/** Set the transport speed.
- * @param speed New speed
- * @param abort
+ * Called from the process thread.
+ * @param speed New speed
*/
void
Session::set_transport_speed (double speed, bool abort, bool clear_state)
{
- DEBUG_TRACE (DEBUG::Transport, string_compose ("Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n", speed, abort, clear_state, _transport_speed));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n",
+ speed, abort, clear_state, _transport_speed, _transport_frame));
if (_transport_speed == speed) {
return;
}
+ if (actively_recording() && speed != 1.0 && speed != 0.0) {
+ /* no varispeed during recording */
+ return;
+ }
+
_target_transport_speed = fabs(speed);
/* 8.0 max speed is somewhat arbitrary but based on guestimates regarding disk i/o capability
/* we are rolling and we want to stop */
- if (Config->get_monitoring_model() == HardwareMonitoring)
- {
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr && tr->record_enabled ()) {
- //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
- tr->monitor_input (true);
- }
- }
+ if (Config->get_monitoring_model() == HardwareMonitoring) {
+ set_track_monitor_input_status (true);
}
if (synced_to_jack ()) {
if (clear_state) {
- /* do this here because our response to the slave won't
+ /* do this here because our response to the slave won't
take care of it.
*/
_play_range = false;
stop_transport (abort);
}
+ unset_play_loop ();
+
} else if (transport_stopped() && speed == 1.0) {
/* we are stopped and we want to start rolling at speed 1 */
- if (Config->get_monitoring_model() == HardwareMonitoring) {
-
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (config.get_auto_input() && tr && tr->record_enabled ()) {
- //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
- tr->monitor_input (false);
- }
- }
+ if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
+ set_track_monitor_input_status (false);
}
if (synced_to_jack()) {
} else {
+ /* not zero, not 1.0 ... varispeed */
+
if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) {
- warning << string_compose (_("Global varispeed cannot be supported while %1 is connected to JACK transport control"),
- PROGRAM_NAME)
+ warning << string_compose (
+ _("Global varispeed cannot be supported while %1 is connected to JACK transport control"),
+ PROGRAM_NAME)
<< endmsg;
return;
}
/* if we are reversing relative to the current speed, or relative to the speed
before the last stop, then we have to do extra work.
*/
-
+
PostTransportWork todo = PostTransportWork (0);
if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0f && speed < 0.0f)) {
add_post_transport_work (todo);
_butler->schedule_transport_work ();
}
+
+ TransportStateChange (); /* EMIT SIGNAL */
}
}
return;
}
- if (actively_recording() && !(transport_sub_state & StopPendingCapture) && _worst_output_latency > current_block_size) {
+ if (actively_recording() && !(transport_sub_state & StopPendingCapture) && worst_input_latency() > current_block_size) {
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (tr) {
tr->prepare_to_stop (_transport_frame);
}
- }
+ }
/* we need to capture the audio that has still not yet been received by the system
at the time the stop is requested, so we have to roll past that time.
and then we'll really be stopped.
*/
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("stop transport requested @ %1, scheduled for + %2 - %3 = %4, abort = %5\n",
+ _transport_frame, _worst_input_latency, current_block_size,
+ _transport_frame - _worst_input_latency - current_block_size,
+ abort));
+
SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace,
- _transport_frame + _worst_output_latency - current_block_size,
- 0, 0, abort);
+ _transport_frame + _worst_input_latency - current_block_size,
+ 0, 0, abort);
merge_event (ev);
transport_sub_state |= StopPendingCapture;
return;
}
-
if ((transport_sub_state & PendingDeclickOut) == 0) {
- if (!(transport_sub_state & StopPendingCapture)) {
+ if (!(transport_sub_state & StopPendingCapture)) {
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
tr->prepare_to_stop (_transport_frame);
}
- }
- }
-
+ }
+ }
+
transport_sub_state |= PendingDeclickOut;
/* we'll be called again after the declick */
pending_abort = abort;
_butler->schedule_transport_work ();
}
+/** Called from the process thread */
void
Session::start_transport ()
{
+ DEBUG_TRACE (DEBUG::Transport, "start_transport\n");
+
_last_roll_location = _transport_frame;
_last_roll_or_reversal_location = _transport_frame;
-
+
have_looped = false;
/* if record status is Enabled, move it to Recording. if its
if (tr) {
tr->realtime_set_speed (tr->speed(), true);
}
- (*i)->automation_snapshot (_transport_frame, true);
+ (*i)->automation_snapshot (_transport_frame, true);
}
- Timecode::Time time;
- timecode_time_subframes (_transport_frame, time);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+ if (!_engine.freewheeling()) {
+ Timecode::Time time;
+ timecode_time_subframes (_transport_frame, time);
+ if (!dynamic_cast<MTC_Slave*>(_slave)) {
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+ }
+ }
TransportStateChange (); /* EMIT SIGNAL */
}
delete _slave;
_slave = new_slave;
+ send_full_time_code (_transport_frame);
+
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
try {
new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24);
}
-
+
catch (failed_constructor& err) {
return;
}
return;
}
- if (config.get_video_pullup() != 0.0f) {
- return;
- }
+ if (config.get_video_pullup() != 0.0f) {
+ return;
+ }
new_slave = new JACK_Slave (_engine.jack());
break;
-
+
default:
new_slave = 0;
break;
request_sync_source (new_slave);
}
-void
-Session::reverse_track_buffers ()
-{
- add_post_transport_work (PostTransportReverse);
- _butler->schedule_transport_work ();
-}
-
void
Session::set_track_speed (Track* track, double speed)
{
/* Called from event-processing context */
unset_play_range ();
-
+
if (range.empty()) {
/* _play_range set to false in unset_play_range()
*/
unset_play_loop ();
list<AudioRange>::size_type sz = range.size();
-
+
if (sz > 1) {
-
- list<AudioRange>::iterator i = range.begin();
+
+ list<AudioRange>::iterator i = range.begin();
list<AudioRange>::iterator next;
-
+
while (i != range.end()) {
-
+
next = i;
++next;
-
+
/* locating/stopping is subject to delays for declicking.
*/
-
+
framepos_t requested_frame = i->end;
-
+
if (requested_frame > current_block_size) {
requested_frame -= current_block_size;
} else {
requested_frame = 0;
}
-
+
if (next == range.end()) {
ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, requested_frame, 0, 0.0f);
} else {
ev = new SessionEvent (SessionEvent::RangeLocate, SessionEvent::Add, requested_frame, (*next).start, 0.0f);
}
-
+
merge_event (ev);
-
+
i = next;
}
-
+
} else if (sz == 1) {
ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, range.front().end, 0, 0.0f);
merge_event (ev);
-
- }
+
+ }
/* save range so we can do auto-return etc. */
ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false);
merge_event (ev);
-
+
TransportStateChange ();
}
void
Session::xrun_recovery ()
{
- Xrun (_transport_frame); //EMIT SIGNAL
+ Xrun (_transport_frame); /* EMIT SIGNAL */
if (Config->get_stop_recording_on_xrun() && actively_recording()) {
void
Session::route_processors_changed (RouteProcessorChange c)
{
- if (c.type == RouteProcessorChange::MeterPointChange) {
+ if (ignore_route_processor_changes) {
return;
}
- update_latency_compensation (false, false);
- resort_routes ();
-}
-
-void
-Session::update_latency_compensation (bool with_stop, bool abort)
-{
- bool update_jack = false;
- PostTransportWork ptw;
-
- if (_state_of_the_state & Deletion) {
+ if (c.type == RouteProcessorChange::MeterPointChange) {
return;
}
- _worst_track_latency = 0;
- ptw = post_transport_work();
-
-#undef DEBUG_LATENCY
-#ifdef DEBUG_LATENCY
- cerr << "\n---------------------------------\nUPDATE LATENCY\n";
-#endif
-
- boost::shared_ptr<RouteList> r = routes.reader ();
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-
- if (with_stop) {
- (*i)->nonrealtime_handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush));
- }
-
- framecnt_t old_latency = (*i)->output()->signal_latency ();
- framecnt_t track_latency = (*i)->update_total_latency ();
-
- if (old_latency != track_latency) {
- (*i)->input()->update_port_total_latencies ();
- (*i)->output()->update_port_total_latencies ();
- update_jack = true;
- }
-
- if (!(*i)->is_hidden() && ((*i)->active())) {
- _worst_track_latency = max (_worst_track_latency, track_latency);
- }
- }
-
- if (update_jack) {
- _engine.update_total_latencies ();
- }
-
-#ifdef DEBUG_LATENCY
- cerr << "\tworst was " << _worst_track_latency << endl;
-#endif
-
- for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- (*i)->set_latency_delay (_worst_track_latency);
- }
-
- set_worst_io_latencies ();
+ update_latency_compensation ();
+ resort_routes ();
- /* reflect any changes in latencies into capture offsets
- */
-
- boost::shared_ptr<RouteList> rl = routes.reader();
- for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
- if (tr) {
- tr->set_capture_offset ();
- }
- }
+ set_dirty ();
}
void
bool
Session::maybe_stop (framepos_t limit)
{
- if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) {
- if (synced_to_jack () && config.get_jack_time_master ()) {
- _engine.transport_stop ();
- } else if (!synced_to_jack ()) {
- stop_transport ();
- }
- return true;
- }
- return false;
+ if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) {
+ if (synced_to_jack () && config.get_jack_time_master ()) {
+ _engine.transport_stop ();
+ } else if (!synced_to_jack ()) {
+ stop_transport ();
+ }
+ return true;
+ }
+ return false;
}
void
Session::send_mmc_locate (framepos_t t)
{
- Timecode::Time time;
- timecode_time_subframes (t, time);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+ if (!_engine.freewheeling()) {
+ Timecode::Time time;
+ timecode_time_subframes (t, time);
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+ }
}
/** Ask the transport to not send timecode until further notice. The suspension