X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_transport.cc;h=946d3ea890af83b57ec2ed94657819321e7dc53b;hb=ad547e53fc168410b22628a8cb125e8d4da4b293;hp=9077ba7cacb097327b92d49c43496e396268c28e;hpb=895bad1dcd817cb8ad32dc255a90271edd6659fb;p=ardour.git diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 9077ba7cac..946d3ea890 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -30,9 +30,9 @@ #include "midi++/mmc.h" #include "midi++/port.h" +#include "midi++/manager.h" #include "ardour/ardour.h" -#include "ardour/audio_diskstream.h" #include "ardour/audioengine.h" #include "ardour/auditioner.h" #include "ardour/butler.h" @@ -107,10 +107,10 @@ Session::request_transport_speed (double speed) } void -Session::request_diskstream_speed (Diskstream& ds, double speed) +Session::request_track_speed (Track* tr, double speed) { - SessionEvent* ev = new SessionEvent (SessionEvent::SetDiskstreamSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed); - ev->set_ptr (&ds); + SessionEvent* ev = new SessionEvent (SessionEvent::SetTrackSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed); + ev->set_ptr (tr); queue_event (ev); } @@ -142,7 +142,7 @@ void Session::request_play_loop (bool yn, bool leave_rolling) { SessionEvent* ev; - Location *location = _locations.auto_loop_location(); + Location *location = _locations->auto_loop_location(); if (location == 0 && yn) { error << _("Cannot loop - no loop range defined") @@ -155,7 +155,7 @@ Session::request_play_loop (bool yn, bool leave_rolling) queue_event (ev); if (!leave_rolling && !yn && Config->get_seamless_loop() && transport_rolling()) { - // request an immediate locate to refresh the diskstreams + // request an immediate locate to refresh the tracks // after disabling looping request_locate (_transport_frame-1, false); } @@ -182,11 +182,6 @@ Session::realtime_stop (bool abort, bool clear_state) /* assume that when we start, we'll be moving forwards */ - // FIXME: where should this really be? [DR] - //send_full_time_code(); - deliver_mmc (MIDI::MachineControl::cmdStop, 0); - deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame); - if (_transport_speed < 0.0f) { todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse)); } else { @@ -230,18 +225,22 @@ Session::realtime_stop (bool abort, bool clear_state) _clear_event_type (SessionEvent::RangeStop); _clear_event_type (SessionEvent::RangeLocate); - disable_record (true); - + /* 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; _target_transport_speed = 0; + g_atomic_int_set (&_playback_load, 100); + g_atomic_int_set (&_capture_load, 100); + if (config.get_use_video_sync()) { waiting_for_sync_offset = true; } - transport_sub_state = ((!config.get_external_sync()&& config.get_auto_return()) ? AutoReturning : 0); + transport_sub_state = 0; } void @@ -251,14 +250,34 @@ Session::butler_transport_work () bool finished; PostTransportWork ptw; boost::shared_ptr r = routes.reader (); - boost::shared_ptr dsl = diskstreams.reader(); int on_entry = g_atomic_int_get (&_butler->should_do_transport_work); finished = true; ptw = post_transport_work(); DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1\n", enum_2_string (ptw))); - + + if (ptw & PostTransportAdjustPlaybackBuffering) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr) { + tr->adjust_playback_buffering (); + /* and refill those buffers ... */ + tr->non_realtime_locate (_transport_frame); + } + } + + } + + if (ptw & PostTransportAdjustCaptureBuffering) { + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr) { + tr->adjust_capture_buffering (); + } + } + } + if (ptw & PostTransportCurveRealloc) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->curve_reallocate(); @@ -266,8 +285,11 @@ Session::butler_transport_work () } if (ptw & PostTransportInputChange) { - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->non_realtime_input_change (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr) { + tr->non_realtime_input_change (); + } } } @@ -285,9 +307,10 @@ Session::butler_transport_work () if (!(ptw & PostTransportLocate)) { - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - (*i)->non_realtime_locate (_transport_frame); + 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); } if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) { /* new request, stop seeking, and start again */ @@ -323,26 +346,30 @@ 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")); } void Session::non_realtime_set_speed () { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->non_realtime_set_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); + if (tr) { + tr->non_realtime_set_speed (); + } } } void Session::non_realtime_overwrite (int on_entry, bool& finished) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->pending_overwrite) { - (*i)->overwrite_existing_buffers (); + 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->pending_overwrite ()) { + tr->overwrite_existing_buffers (); } if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) { finished = false; @@ -355,10 +382,12 @@ Session::non_realtime_overwrite (int on_entry, bool& finished) void Session::non_realtime_locate () { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->non_realtime_locate (_transport_frame); + 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); + } } } @@ -375,10 +404,10 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) did_record = false; saved = false; - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->get_captured_frames () != 0) { + 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->get_captured_frames () != 0) { did_record = true; break; } @@ -399,42 +428,27 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) if (did_record) { begin_reversible_command ("capture"); + _have_captured = true; + } - Location* loc = _locations.session_range_location(); - bool change_end = false; - - if (_transport_frame < loc->end()) { - - /* stopped recording before current end */ - - if (config.get_end_marker_is_free()) { - - /* first capture for this session, move end back to where we are */ - - change_end = true; - } - - } else if (_transport_frame > loc->end()) { - - /* stopped recording after the current end, extend it */ + DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n")); - change_end = true; - } + 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 (change_end) { - XMLNode &before = loc->get_state(); - loc->set_end(_transport_frame); - XMLNode &after = loc->get_state(); - add_command (new MementoCommand(*loc, &before, &after)); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr) { + tr->transport_stopped_wallclock (*now, xnow, abort); } - - config.set_end_marker_is_free (false); - _have_captured = true; } - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->transport_stopped (*now, xnow, abort); - } + if (abort && did_record) { + _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup); + } boost::shared_ptr r = routes.reader (); @@ -467,7 +481,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) 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; @@ -489,7 +503,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) if (!synced_to_jack()) { - Location *location = _locations.auto_loop_location(); + Location *location = _locations->auto_loop_location(); if (location != 0) { _transport_frame = location->start(); @@ -527,7 +541,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } - /* do this before seeking, because otherwise the Diskstreams will do the wrong thing in seamless loop mode. + /* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode. */ if (ptw & PostTransportClearSubstate) { @@ -537,11 +551,14 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) /* this for() block can be put inside the previous if() and has the effect of ... ??? what */ - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - (*i)->non_realtime_locate (_transport_frame); + 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); } + if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) { finished = false; /* we will be back */ @@ -551,9 +568,9 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) have_looped = false; - send_full_time_code (0); - deliver_mmc (MIDI::MachineControl::cmdStop, 0); - deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame); + send_full_time_code (_transport_frame); + 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 */ @@ -571,10 +588,6 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) save_state (_current_snapshot_name); } - if (ptw & PostTransportDuration) { - DurationChanged (); /* EMIT SIGNAL */ - } - if (ptw & PostTransportStop) { _play_range = false; play_loop = false; @@ -623,11 +636,12 @@ Session::unset_play_loop () play_loop = false; clear_events (SessionEvent::AutoLoop); - // set all diskstreams to NOT use internal looping - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - (*i)->set_loop (0); + // set all tracks to NOT use internal 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->hidden()) { + tr->set_loop (0); } } } @@ -639,13 +653,11 @@ Session::set_play_loop (bool yn) Location *loc; - if (yn == play_loop || (actively_recording() && yn) || (loc = _locations.auto_loop_location()) == 0) { + if (yn == play_loop || (actively_recording() && yn) || (loc = _locations->auto_loop_location()) == 0) { /* nothing to do, or can't change loop status while recording */ return; } - set_dirty(); - 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) @@ -662,20 +674,22 @@ Session::set_play_loop (bool yn) unset_play_range (); if (Config->get_seamless_loop()) { - // set all diskstreams to use internal looping - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - (*i)->set_loop (loc); + // set all tracks to use internal 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->hidden()) { + tr->set_loop (loc); } } } else { - // set all diskstreams to NOT use internal looping - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - (*i)->set_loop (0); + // set all tracks to NOT use internal 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->hidden()) { + tr->set_loop (0); } } } @@ -741,24 +755,28 @@ Session::start_locate (nframes64_t target_frame, bool with_roll, bool with_flush int Session::micro_locate (nframes_t distance) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->can_internal_playback_seek (distance)) { + 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->can_internal_playback_seek (distance)) { return -1; } } - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->internal_playback_seek (distance); + for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr) { + tr->internal_playback_seek (distance); + } } _transport_frame += distance; return 0; } +/** @param with_mmc true to send a MMC locate command when the locate is done */ void -Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force) +Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force, bool with_mmc) { if (actively_recording() && !with_loop) { return; @@ -769,6 +787,7 @@ Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool set_transport_speed (1.0, false); } loop_changing = false; + Located (); /* EMIT SIGNAL */ return; } @@ -826,24 +845,25 @@ Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool /* switch from input if we're going to roll */ if (Config->get_monitoring_model() == HardwareMonitoring) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { + 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 ()) { //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (!config.get_auto_input()); + tr->monitor_input (!config.get_auto_input()); } } } } else { /* otherwise we're going to stop, so do the opposite */ if (Config->get_monitoring_model() == HardwareMonitoring) { - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { + 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 ()) { //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (true); + tr->monitor_input (true); } } } @@ -851,7 +871,7 @@ Session::locate (nframes64_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(); + 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 @@ -861,12 +881,12 @@ Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool if (with_loop) { // this is only necessary for seamless looping - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { + 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 - (*i)->transport_looped(_transport_frame); + tr->transport_looped(_transport_frame); } } } @@ -879,6 +899,10 @@ Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool _send_timecode_update = true; + if (with_mmc) { + send_mmc_locate (_transport_frame); + } + Located (); /* EMIT SIGNAL */ } @@ -913,12 +937,12 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) if (Config->get_monitoring_model() == HardwareMonitoring) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->record_enabled ()) { + 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 ()) { //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (true); + tr->monitor_input (true); } } } @@ -942,12 +966,12 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) if (Config->get_monitoring_model() == HardwareMonitoring) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (config.get_auto_input() && (*i)->record_enabled ()) { + 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 (config.get_auto_input() && tr && tr->record_enabled ()) { //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl; - (*i)->monitor_input (false); + tr->monitor_input (false); } } } @@ -989,14 +1013,16 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0f && speed < 0.0f)) { todo = PostTransportWork (todo | PostTransportReverse); + _last_roll_or_reversal_location = _transport_frame; } _last_transport_speed = _transport_speed; _transport_speed = speed; - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if ((*i)->realtime_set_speed ((*i)->speed(), true)) { + 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->realtime_set_speed (tr->speed(), true)) { todo = PostTransportWork (todo | PostTransportSpeed); break; } @@ -1020,10 +1046,12 @@ Session::stop_transport (bool abort, bool clear_state) if (actively_recording() && !(transport_sub_state & StopPendingCapture) && _worst_output_latency > current_block_size) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->prepare_to_stop (_transport_frame); + 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->prepare_to_stop (_transport_frame); + } } /* we need to capture the audio that has still not yet been received by the system @@ -1048,10 +1076,12 @@ Session::stop_transport (bool abort, bool clear_state) if ((transport_sub_state & PendingDeclickOut) == 0) { if (!(transport_sub_state & StopPendingCapture)) { - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->prepare_to_stop (_transport_frame); + 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->prepare_to_stop (_transport_frame); + } } } @@ -1069,6 +1099,8 @@ void Session::start_transport () { _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 @@ -1097,12 +1129,18 @@ Session::start_transport () _transport_speed = 1.0; _target_transport_speed = 1.0; - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->realtime_set_speed ((*i)->speed(), true); + 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->realtime_set_speed (tr->speed(), true); + } + (*i)->automation_snapshot (_transport_frame, true); } - deliver_mmc(MIDI::MachineControl::cmdDeferredPlay, _transport_frame); + Timecode::Time time; + timecode_time_subframes (_transport_frame, time); + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay)); TransportStateChange (); /* EMIT SIGNAL */ } @@ -1177,13 +1215,14 @@ Session::use_sync_source (Slave* new_slave) delete _slave; _slave = new_slave; - boost::shared_ptr dsl = diskstreams.reader(); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - if ((*i)->realtime_set_speed ((*i)->speed(), true)) { + 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->hidden()) { + if (tr->realtime_set_speed (tr->speed(), true)) { non_rt_required = true; } - (*i)->set_slaved (_slave != 0); + tr->set_slaved (_slave != 0); } } @@ -1214,17 +1253,11 @@ Session::switch_to_sync_source (SyncSource src) return; } - if (_mtc_port) { - try { - new_slave = new MTC_Slave (*this, *_mtc_port); - } - - catch (failed_constructor& err) { - return; - } + try { + new_slave = new MTC_Slave (*this, *MIDI::Manager::instance()->mtc_input_port()); + } - } else { - error << _("No MTC port defined: MTC slaving is impossible.") << endmsg; + catch (failed_constructor& err) { return; } break; @@ -1234,17 +1267,11 @@ Session::switch_to_sync_source (SyncSource src) return; } - if (_midi_clock_port) { - try { - new_slave = new MIDIClock_Slave (*this, *_midi_clock_port, 24); - } - - catch (failed_constructor& err) { - return; - } - - } else { - error << _("No MIDI Clock port defined: MIDI Clock slaving is impossible.") << endmsg; + try { + new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24); + } + + catch (failed_constructor& err) { return; } break; @@ -1254,6 +1281,10 @@ Session::switch_to_sync_source (SyncSource src) return; } + if (config.get_video_pullup() != 0.0f) { + return; + } + new_slave = new JACK_Slave (_engine.jack()); break; @@ -1266,16 +1297,16 @@ Session::switch_to_sync_source (SyncSource src) } void -Session::reverse_diskstream_buffers () +Session::reverse_track_buffers () { add_post_transport_work (PostTransportReverse); _butler->schedule_transport_work (); } void -Session::set_diskstream_speed (Diskstream* stream, double speed) +Session::set_track_speed (Track* track, double speed) { - if (stream->realtime_set_speed (speed, false)) { + if (track->realtime_set_speed (speed, false)) { add_post_transport_work (PostTransportSpeed); _butler->schedule_transport_work (); set_dirty (); @@ -1493,11 +1524,13 @@ Session::update_latency_compensation (bool with_stop, bool abort) /* reflect any changes in latencies into capture offsets */ - - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->set_capture_offset (); + + 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->set_capture_offset (); + } } } @@ -1530,3 +1563,35 @@ Session::maybe_stop (nframes_t limit) } return false; } + +void +Session::send_mmc_locate (nframes64_t t) +{ + 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 + * will come into effect some finite time after this call, and timecode_transmission_suspended() + * should be checked by the caller to find out when. + */ +void +Session::request_suspend_timecode_transmission () +{ + SessionEvent* ev = new SessionEvent (SessionEvent::SetTimecodeTransmission, SessionEvent::Add, SessionEvent::Immediate, 0, 0, false); + queue_event (ev); +} + +void +Session::request_resume_timecode_transmission () +{ + SessionEvent* ev = new SessionEvent (SessionEvent::SetTimecodeTransmission, SessionEvent::Add, SessionEvent::Immediate, 0, 0, true); + queue_event (ev); +} + +bool +Session::timecode_transmission_suspended () const +{ + return g_atomic_int_get (&_suspend_timecode_transmission) == 1; +}