X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_transport.cc;h=38ad5212359a7377e2e5cf045a11197b83fdc6ab;hb=16a8762dd494adb68756b8d549f349ed7bdccd9b;hp=84699ac71810264b30965e8468d338e018df2c29;hpb=508c5eb5bd7e31d721c3a29fc734aab3a44aa8a9;p=ardour.git diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 84699ac718..38ad521235 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -17,14 +17,13 @@ */ -#include -#include -#include - #ifdef WAF_BUILD #include "libardour-config.h" #endif +#include +#include +#include #include "pbd/undo.h" #include "pbd/error.h" @@ -34,9 +33,7 @@ #include "midi++/mmc.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" @@ -89,7 +86,7 @@ Session::request_sync_source (Slave* new_slave) seamless = Config->get_seamless_loop (); - if (dynamic_cast(new_slave)) { + if (dynamic_cast(new_slave)) { /* JACK cannot support seamless looping at present */ Config->set_seamless_loop (false); } else { @@ -101,14 +98,16 @@ Session::request_sync_source (Slave* new_slave) _was_seamless = seamless; ev->slave = new_slave; + DEBUG_TRACE (DEBUG::Slave, "sent request for new slave\n"); queue_event (ev); } void -Session::request_transport_speed (double speed) +Session::request_transport_speed (double speed, bool as_default) { SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed); - DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport speed = %1\n", speed)); + ev->third_yes_or_no = true; + DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport speed = %1 as default = %2\n", speed, as_default)); queue_event (ev); } @@ -117,13 +116,13 @@ Session::request_transport_speed (double speed) * 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) +Session::request_transport_speed_nonzero (double speed, bool as_default) { if (speed == 0) { speed = DBL_EPSILON; } - request_transport_speed (speed); + request_transport_speed (speed, as_default); } void @@ -204,6 +203,7 @@ Session::realtime_stop (bool abort, bool clear_state) if (_transport_speed < 0.0f) { todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse)); + _default_transport_speed = 1.0; } else { todo = PostTransportWork (todo | PostTransportStop); } @@ -300,8 +300,8 @@ Session::butler_transport_work () if (tr) { tr->adjust_playback_buffering (); /* and refill those buffers ... */ - tr->non_realtime_locate (_transport_frame); } + (*i)->non_realtime_locate (_transport_frame); } } @@ -345,10 +345,8 @@ Session::butler_transport_work () if (!(ptw & PostTransportLocate)) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->hidden()) { - tr->non_realtime_locate (_transport_frame); - } + (*i)->non_realtime_locate (_transport_frame); + if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) { /* new request, stop seeking, and start again */ g_atomic_int_dec_and_test (&_butler->should_do_transport_work); @@ -385,6 +383,7 @@ Session::butler_transport_work () g_atomic_int_dec_and_test (&_butler->should_do_transport_work); DEBUG_TRACE (DEBUG::Transport, X_("Butler transport work all done\n")); + DEBUG_TRACE (DEBUG::Transport, X_(string_compose ("Frame %1\n", _transport_frame))); } void @@ -421,10 +420,7 @@ Session::non_realtime_locate () { boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr) { - tr->non_realtime_locate (_transport_frame); - } + (*i)->non_realtime_locate (_transport_frame); } /* XXX: it would be nice to generate the new clicks here (in the non-RT thread) @@ -465,7 +461,6 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) auditioner->cancel_audition (); } - clear_clicks(); cumulative_rf_motion = 0; reset_rf_scale (0); @@ -496,7 +491,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_hidden()) { + if (!(*i)->is_auditioner()) { (*i)->set_pending_declick (0); } } @@ -519,13 +514,13 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) if (auto_return_enabled || (ptw & PostTransportLocate) || (_requested_return_frame >= 0) || - synced_to_jack()) { + synced_to_engine()) { if (pending_locate_flush) { flush_all_inserts (); } - if ((auto_return_enabled || synced_to_jack() || _requested_return_frame >= 0) && + if ((auto_return_enabled || synced_to_engine() || _requested_return_frame >= 0) && !(ptw & PostTransportLocate)) { /* no explicit locate queued */ @@ -548,7 +543,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) /* don't try to handle loop play when synced to JACK */ - if (!synced_to_jack()) { + if (!synced_to_engine()) { Location *location = _locations->auto_loop_location(); @@ -588,6 +583,8 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } + clear_clicks(); + /* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode. */ @@ -601,10 +598,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: locate\n")); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", (*i)->name())); - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->hidden()) { - tr->non_realtime_locate (_transport_frame); - } + (*i)->non_realtime_locate (_transport_frame); if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) { finished = false; @@ -615,17 +609,41 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) have_looped = false; - send_full_time_code (_transport_frame); + /* don't bother with this stuff if we're disconnected from the engine, + because there will be no process callbacks to deliver stuff from + */ - if (!dynamic_cast(_slave)) { - MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop)); - send_mmc_locate (_transport_frame); + if (_engine.connected() && !_engine.freewheeling()) { + // need to queue this in the next RT cycle + _send_timecode_update = true; + + if (!dynamic_cast(_slave)) { + _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop)); + + /* This (::non_realtime_stop()) gets called by main + process thread, which will lead to confusion + when calling AsyncMIDIPort::write(). + + Something must be done. XXX + */ + send_mmc_locate (_transport_frame); + } } if ((ptw & PostTransportLocate) && get_record_enabled()) { - /* capture start has been changed, so save pending state */ - save_state ("", true); - saved = true; + /* This is scheduled by realtime_stop(), which is also done + * when a slave requests /locate/ for an initial sync. + * We can't hold up the slave for long with a save() here, + * without breaking its initial sync cycle. + * + * save state only if there's no slave or if it's not yet locked. + */ + if (!_slave || !_slave->locked()) { + DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: pending save\n")); + /* capture start has been changed, so save pending state */ + save_state ("", true); + saved = true; + } } /* always try to get rid of this */ @@ -644,14 +662,19 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } PositionChanged (_transport_frame); /* EMIT SIGNAL */ + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC with speed = %1\n", _transport_speed)); TransportStateChange (); /* EMIT SIGNAL */ /* and start it up again if relevant */ if ((ptw & PostTransportLocate) && !config.get_external_sync() && pending_locate_roll) { request_transport_speed (1.0); - pending_locate_roll = false; } + + /* Even if we didn't do a pending locate roll this time, we don't want it hanging + around for next time. + */ + pending_locate_roll = false; } void @@ -661,7 +684,7 @@ Session::check_declick_out () /* this is called after a process() iteration. if PendingDeclickOut was set, it means that we were waiting to declick the output (which has just been - done) before doing something else. this is where we do that "something else". + done) before maybe doing something else. this is where we do that "something else". note: called from the audio thread. */ @@ -675,6 +698,10 @@ Session::check_declick_out () stop_transport (pending_abort); transport_sub_state &= ~(PendingDeclickOut|PendingLocate); } + + } else if (transport_sub_state & PendingLoopDeclickOut) { + /* Nothing else to do here; we've declicked, and the loop event will be along shortly */ + transport_sub_state &= ~PendingLoopDeclickOut; } } @@ -683,6 +710,7 @@ Session::unset_play_loop () { play_loop = false; clear_events (SessionEvent::AutoLoop); + clear_events (SessionEvent::AutoLoopDeclick); // set all tracks to NOT use internal looping boost::shared_ptr rl = routes.reader (); @@ -706,7 +734,7 @@ Session::set_play_loop (bool yn) return; } - if (yn && Config->get_seamless_loop() && synced_to_jack()) { + if (yn && Config->get_seamless_loop() && synced_to_engine()) { warning << string_compose ( _("Seamless looping cannot be supported while %1 is using JACK transport.\n" "Recommend changing the configured options"), PROGRAM_NAME) @@ -743,13 +771,20 @@ Session::set_play_loop (bool yn) } } - /* put the loop event into the event list */ + /* Put the delick and loop events in into the event list. The declick event will + cause a de-clicking fade-out just before the end of the loop, and it will also result + in a fade-in when the loop restarts. The AutoLoop event will peform the actual loop. + */ + + framepos_t dcp; + framecnt_t dcl; + auto_loop_declick_range (loc, dcp, dcl); + merge_event (new SessionEvent (SessionEvent::AutoLoopDeclick, SessionEvent::Replace, dcp, dcl, 0.0f)); + merge_event (new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f)); - SessionEvent* event = new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f); - merge_event (event); + /* locate to start of loop and roll. - /* locate to start of loop and roll. If doing seamless loop, force a - locate+buffer refill even if we are positioned there already. + args: positition, roll=true, flush=true, with_loop=false, force buffer refill if seamless looping */ start_locate (loc->start(), true, true, false, Config->get_seamless_loop()); @@ -760,6 +795,7 @@ Session::set_play_loop (bool yn) unset_play_loop (); } + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC2 with speed = %1\n", _transport_speed)); TransportStateChange (); } void @@ -775,7 +811,7 @@ Session::flush_all_inserts () void Session::start_locate (framepos_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force) { - if (synced_to_jack()) { + if (synced_to_engine()) { double sp; framepos_t pos; @@ -784,6 +820,14 @@ Session::start_locate (framepos_t target_frame, bool with_roll, bool with_flush, if (target_frame != pos) { + if (config.get_jack_time_master()) { + /* actually locate now, since otherwise jack_timebase_callback + will use the incorrect _transport_frame and report an old + and incorrect time to Jack transport + */ + locate (target_frame, with_roll, with_flush, with_loop, force); + } + /* tell JACK to change transport position, and we will follow along later in ::follow_slave() */ @@ -825,13 +869,21 @@ Session::micro_locate (framecnt_t distance) /** @param with_mmc true to send a MMC locate command when the locate is done */ void -Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force, bool with_mmc) +Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool for_seamless_loop, bool force, bool with_mmc) { - if (actively_recording() && !with_loop) { + /* Locates for seamless looping are fairly different from other + * locates. They assume that the diskstream buffers for each track + * already have the correct data in them, and thus there is no need to + * actually tell the tracks to locate. What does need to be done, + * though, is all the housekeeping that is associated with non-linear + * changes in the value of _transport_frame. + */ + + if (actively_recording() && !for_seamless_loop) { return; } - if (!force && _transport_frame == target_frame && !loop_changing && !with_loop) { + if (!force && _transport_frame == target_frame && !loop_changing && !for_seamless_loop) { if (with_roll) { set_transport_speed (1.0, false); } @@ -840,8 +892,11 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool return; } - if (_transport_speed) { - /* schedule a declick. we'll be called again when its done */ + if (_transport_speed && !for_seamless_loop) { + /* Schedule a declick. We'll be called again when its done. + We only do it this way for ordinary locates, not those + due to **seamless** loops. + */ if (!(transport_sub_state & PendingDeclickOut)) { transport_sub_state |= (PendingDeclickOut|PendingLocate); @@ -855,6 +910,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool // Update Timecode time // [DR] FIXME: find out exactly where this should go below _transport_frame = target_frame; + _last_roll_or_reversal_location = target_frame; timecode_time(_transport_frame, transmitting_timecode_time); outbound_mtc_timecode_frame = _transport_frame; next_quarter_frame_to_send = 0; @@ -868,18 +924,21 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool * */ - if (transport_rolling() && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) { + bool transport_was_stopped = !transport_rolling(); + + if (transport_was_stopped && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_engine() && play_loop)) { realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct + transport_was_stopped = true; } else { /* otherwise tell the world that we located */ realtime_locate (); } - if (force || !with_loop || loop_changing) { + if (force || !for_seamless_loop || loop_changing) { PostTransportWork todo = PostTransportLocate; - if (with_roll) { + if (with_roll && transport_was_stopped) { todo = PostTransportWork (todo | PostTransportRoll); } @@ -890,7 +949,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool /* this is functionally what clear_clicks() does but with a tentative lock */ - Glib::RWLock::WriterLock clickm (click_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::WriterLock clickm (click_lock, Glib::Threads::TRY_LOCK); if (clickm.locked()) { @@ -916,27 +975,39 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool /* cancel looped playback if transport pos outside of loop range */ if (play_loop) { + Location* al = _locations->auto_loop_location(); - if (al && (_transport_frame < al->start() || _transport_frame > al->end())) { - // cancel looping directly, this is called from event handling context - set_play_loop (false); - } - else if (al && _transport_frame == al->start()) { - if (with_loop) { - // this is only necessary for seamless looping + if (al) { + if (_transport_frame < al->start() || _transport_frame > al->end()) { - boost::shared_ptr rl = routes.reader(); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && tr->record_enabled ()) { - // tell it we've looped, so it can deal with the record state - tr->transport_looped(_transport_frame); + // located outside the loop: cancel looping directly, this is called from event handling context + + set_play_loop (false); + + } else if (_transport_frame == al->start()) { + + // located to start of loop - this is looping, basically + + if (for_seamless_loop) { + + // this is only necessary for seamless looping + + boost::shared_ptr rl = routes.reader(); + + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + + if (tr && tr->record_enabled ()) { + // tell it we've looped, so it can deal with the record state + tr->transport_looped (_transport_frame); + } } } + + have_looped = true; + TransportLooped(); // EMIT SIGNAL } - have_looped = true; - TransportLooped(); // EMIT SIGNAL } } @@ -948,6 +1019,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool send_mmc_locate (_transport_frame); } + _last_roll_location = _last_roll_or_reversal_location = _transport_frame; Located (); /* EMIT SIGNAL */ } @@ -956,17 +1028,22 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool * @param speed New speed */ void -Session::set_transport_speed (double speed, bool abort, bool clear_state) +Session::set_transport_speed (double speed, bool abort, bool clear_state, bool as_default) { - 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)); + DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4 as_default %6\n", + speed, abort, clear_state, _transport_speed, _transport_frame, as_default)); if (_transport_speed == speed) { + if (as_default && speed == 0.0) { // => reset default transport speed. hacky or what? + _default_transport_speed = 1.0; + } return; } if (actively_recording() && speed != 1.0 && speed != 0.0) { /* no varispeed during recording */ + DEBUG_TRACE (DEBUG::Transport, string_compose ("No varispeed during recording cur_speed %1, frame %2\n", + _transport_speed, _transport_frame)); return; } @@ -990,7 +1067,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) set_track_monitor_input_status (true); } - if (synced_to_jack ()) { + if (synced_to_engine ()) { if (clear_state) { /* do this here because our response to the slave won't take care of it. @@ -1013,7 +1090,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) set_track_monitor_input_status (false); } - if (synced_to_jack()) { + if (synced_to_engine()) { _engine.transport_start (); } else { start_transport (); @@ -1023,7 +1100,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) /* not zero, not 1.0 ... varispeed */ - if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) { + if ((synced_to_engine()) && speed != 0.0 && speed != 1.0) { warning << string_compose ( _("Global varispeed cannot be supported while %1 is connected to JACK transport control"), PROGRAM_NAME) @@ -1059,6 +1136,10 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) _last_transport_speed = _transport_speed; _transport_speed = speed; + if (as_default) { + _default_transport_speed = speed; + } + boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); @@ -1072,6 +1153,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) _butler->schedule_transport_work (); } + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC3 with speed = %1\n", _transport_speed)); TransportStateChange (); /* EMIT SIGNAL */ } } @@ -1174,8 +1256,8 @@ Session::start_transport () transport_sub_state |= PendingDeclickIn; - _transport_speed = 1.0; - _target_transport_speed = 1.0; + _transport_speed = _default_transport_speed; + _target_transport_speed = _transport_speed; boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { @@ -1183,15 +1265,17 @@ Session::start_transport () if (tr) { tr->realtime_set_speed (tr->speed(), true); } - (*i)->automation_snapshot (_transport_frame, true); } - Timecode::Time time; - timecode_time_subframes (_transport_frame, time); - if (!dynamic_cast(_slave)) { - 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(_slave)) { + _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay)); + } } + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC4 with speed = %1\n", _transport_speed)); TransportStateChange (); /* EMIT SIGNAL */ } @@ -1220,7 +1304,6 @@ Session::post_transport () if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) { start_transport (); - } else { transport_sub_state = 0; } @@ -1265,7 +1348,10 @@ Session::use_sync_source (Slave* new_slave) delete _slave; _slave = new_slave; - send_full_time_code (_transport_frame); + DEBUG_TRACE (DEBUG::Slave, string_compose ("set new slave to %1\n", _slave)); + + // need to queue this for next process() cycle + _send_timecode_update = true; boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { @@ -1306,12 +1392,27 @@ Session::switch_to_sync_source (SyncSource src) } try { - new_slave = new MTC_Slave (*this, *MIDI::Manager::instance()->mtc_input_port()); + new_slave = new MTC_Slave (*this, *_midi_ports->mtc_input_port()); + } + + catch (failed_constructor& err) { + return; + } + break; + + case LTC: + if (_slave && dynamic_cast(_slave)) { + return; + } + + try { + new_slave = new LTC_Slave (*this); } catch (failed_constructor& err) { return; } + break; case MIDIClock: @@ -1320,7 +1421,7 @@ Session::switch_to_sync_source (SyncSource src) } try { - new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24); + new_slave = new MIDIClock_Slave (*this, *_midi_ports->midi_clock_input_port(), 24); } catch (failed_constructor& err) { @@ -1328,8 +1429,8 @@ Session::switch_to_sync_source (SyncSource src) } break; - case JACK: - if (_slave && dynamic_cast(_slave)) { + case Engine: + if (_slave && dynamic_cast(_slave)) { return; } @@ -1337,7 +1438,7 @@ Session::switch_to_sync_source (SyncSource src) return; } - new_slave = new JACK_Slave (_engine.jack()); + new_slave = new Engine_Slave (*AudioEngine::instance()); break; default: @@ -1441,6 +1542,7 @@ Session::set_play_range (list& range, bool leave_rolling) ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false); merge_event (ev); + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC5 with speed = %1\n", _transport_speed)); TransportStateChange (); } @@ -1473,14 +1575,17 @@ Session::engine_halted () the picture. */ - g_atomic_int_set (&_butler->should_do_transport_work, 0); - set_post_transport_work (PostTransportWork (0)); - _butler->stop (); + if (_butler) { + g_atomic_int_set (&_butler->should_do_transport_work, 0); + set_post_transport_work (PostTransportWork (0)); + _butler->stop (); + } realtime_stop (false, true); non_realtime_stop (false, 0, ignored); transport_sub_state = 0; + DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC6 with speed = %1\n", _transport_speed)); TransportStateChange (); /* EMIT SIGNAL */ } @@ -1523,23 +1628,13 @@ Session::allow_auto_play (bool yn) auto_play_legal = yn; } -void -Session::reset_jack_connection (jack_client_t* jack) -{ - JACK_Slave* js; - - if (_slave && ((js = dynamic_cast (_slave)) != 0)) { - js->reset_client (jack); - } -} - 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 ()) { + if (synced_to_engine () && config.get_jack_time_master ()) { _engine.transport_stop (); - } else if (!synced_to_jack ()) { + } else if (!synced_to_engine ()) { stop_transport (); } return true; @@ -1550,9 +1645,11 @@ Session::maybe_stop (framepos_t limit) 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); + _mmc->send (MIDI::MachineControlCommand (time)); + } } /** Ask the transport to not send timecode until further notice. The suspension