X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_transport.cc;h=6d7d3b4521c9abdea1c1d1018e8d685081b53685;hb=581376e0ed7d8934b384766ee4500dd6abeb755d;hp=b7377d7dc60d3b834158c40aaf01e00a0a500d62;hpb=498dfebcca3b97cdd7b83444040042fe06626241;p=ardour.git diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index b7377d7dc6..6d7d3b4521 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -21,24 +21,21 @@ #include #include -#include -#include #include "pbd/undo.h" #include "pbd/error.h" -#include +#include "pbd/enumwriter.h" #include "pbd/pthread_utils.h" #include "pbd/memento_command.h" -#include "pbd/stacktrace.h" #include "midi++/mmc.h" #include "midi++/port.h" #include "ardour/ardour.h" -#include "ardour/audio_diskstream.h" #include "ardour/audioengine.h" #include "ardour/auditioner.h" #include "ardour/butler.h" +#include "ardour/debug.h" #include "ardour/location.h" #include "ardour/session.h" #include "ardour/slave.h" @@ -47,7 +44,6 @@ using namespace std; using namespace ARDOUR; -using namespace sigc; using namespace PBD; void @@ -73,20 +69,20 @@ void Session::request_input_change_handling () { if (!(_state_of_the_state & (InitialConnecting|Deletion))) { - Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0); + SessionEvent* ev = new SessionEvent (SessionEvent::InputConfigurationChange, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); queue_event (ev); } } void -Session::request_sync_source (SyncSource src) +Session::request_sync_source (Slave* new_slave) { - Event* ev = new Event (Event::SetSyncSource, Event::Add, Event::Immediate, 0, 0.0); + SessionEvent* ev = new SessionEvent (SessionEvent::SetSyncSource, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); bool seamless; seamless = Config->get_seamless_loop (); - if (src == JACK) { + if (dynamic_cast(new_slave)) { /* JACK cannot support seamless looping at present */ Config->set_seamless_loop (false); } else { @@ -97,50 +93,54 @@ Session::request_sync_source (SyncSource src) /* save value of seamless from before the switch */ _was_seamless = seamless; - ev->sync_source = src; + ev->slave = new_slave; queue_event (ev); } void Session::request_transport_speed (double speed) { - Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, speed); + SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed); + DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport speed = %1\n", speed)); queue_event (ev); } void -Session::request_diskstream_speed (Diskstream& ds, double speed) +Session::request_track_speed (Track* tr, double speed) { - Event* ev = new Event (Event::SetDiskstreamSpeed, Event::Add, Event::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); } void Session::request_stop (bool abort, bool clear_state) { - Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0, abort, clear_state); + SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0, abort, clear_state); + DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport stop, abort = %1, clear state = %2\n", abort, clear_state)); queue_event (ev); } void Session::request_locate (nframes_t target_frame, bool with_roll) { - Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, false); + SessionEvent *ev = new SessionEvent (with_roll ? SessionEvent::LocateRoll : SessionEvent::Locate, SessionEvent::Add, SessionEvent::Immediate, target_frame, 0, false); + DEBUG_TRACE (DEBUG::Transport, string_compose ("Request locate to %1\n", target_frame)); queue_event (ev); } void Session::force_locate (nframes64_t target_frame, bool with_roll) { - Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, true); + SessionEvent *ev = new SessionEvent (with_roll ? SessionEvent::LocateRoll : SessionEvent::Locate, SessionEvent::Add, SessionEvent::Immediate, target_frame, 0, true); + DEBUG_TRACE (DEBUG::Transport, string_compose ("Request forced locate to %1\n", target_frame)); queue_event (ev); } void Session::request_play_loop (bool yn, bool leave_rolling) { - Event* ev; + SessionEvent* ev; Location *location = _locations.auto_loop_location(); if (location == 0 && yn) { @@ -149,11 +149,12 @@ Session::request_play_loop (bool yn, bool leave_rolling) return; } - ev = new Event (Event::SetLoop, Event::Add, Event::Immediate, 0, (leave_rolling ? 1.0 : 0.0), yn); + ev = new SessionEvent (SessionEvent::SetLoop, SessionEvent::Add, SessionEvent::Immediate, 0, (leave_rolling ? 1.0 : 0.0), yn); + DEBUG_TRACE (DEBUG::Transport, string_compose ("Request set loop = %1, leave rolling ? %2\n", yn, 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); } @@ -162,18 +163,20 @@ Session::request_play_loop (bool yn, bool leave_rolling) void Session::request_play_range (list* range, bool leave_rolling) { - Event* ev = new Event (Event::SetPlayAudioRange, Event::Add, Event::Immediate, 0, (leave_rolling ? 1.0 : 0.0)); + SessionEvent* ev = new SessionEvent (SessionEvent::SetPlayAudioRange, SessionEvent::Add, SessionEvent::Immediate, 0, (leave_rolling ? 1.0 : 0.0)); if (range) { ev->audio_range = *range; } else { ev->audio_range.clear (); } + DEBUG_TRACE (DEBUG::Transport, string_compose ("Request play range, leave rolling ? %1\n", leave_rolling)); queue_event (ev); } void Session::realtime_stop (bool abort, bool clear_state) { + DEBUG_TRACE (DEBUG::Transport, "realtime stop\n"); PostTransportWork todo = PostTransportWork (0); /* assume that when we start, we'll be moving forwards */ @@ -193,10 +196,17 @@ Session::realtime_stop (bool abort, bool clear_state) /* move the transport position back to where the request for a stop was noticed. we rolled - past that point to pick up delayed input. + past that point to pick up delayed input (and/or to delick) */ - decrement_transport_position (_worst_output_latency); + 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); + } /* the duration change is not guaranteed to have happened, but is likely */ @@ -215,12 +225,13 @@ Session::realtime_stop (bool abort, bool clear_state) add_post_transport_work (todo); } - _clear_event_type (Event::StopOnce); - _clear_event_type (Event::RangeStop); - _clear_event_type (Event::RangeLocate); - - disable_record (true); + _clear_event_type (SessionEvent::StopOnce); + _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 */ + disable_record (true, (!Config->get_latched_record_enable() && clear_state)); + reset_slave_state (); _transport_speed = 0; @@ -240,11 +251,13 @@ 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 & PostTransportCurveRealloc) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->curve_reallocate(); @@ -252,8 +265,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 (); + } } } @@ -271,9 +287,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 */ @@ -309,26 +326,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; @@ -341,10 +362,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); + } } } @@ -361,10 +384,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; } @@ -386,7 +409,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) if (did_record) { begin_reversible_command ("capture"); - Location* loc = _locations.end_location(); + Location* loc = _locations.session_range_location(); bool change_end = false; if (_transport_frame < loc->end()) { @@ -418,8 +441,12 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) _have_captured = true; } - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->transport_stopped (*now, xnow, abort); + DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n")); + 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); + } } boost::shared_ptr r = routes.reader (); @@ -453,7 +480,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; @@ -513,7 +540,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) { @@ -523,11 +550,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 */ @@ -566,7 +596,9 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) play_loop = false; } - PositionChanged ((nframes64_t) _transport_frame); /* EMIT SIGNAL */ + // can't cast away volatile so copy and emit that + nframes64_t tframe = _transport_frame; + PositionChanged (tframe); /* EMIT SIGNAL */ TransportStateChange (); /* EMIT SIGNAL */ /* and start it up again if relevant */ @@ -605,13 +637,14 @@ void Session::unset_play_loop () { play_loop = false; - clear_events (Event::AutoLoop); + 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); } } } @@ -631,8 +664,8 @@ Session::set_play_loop (bool yn) set_dirty(); if (yn && Config->get_seamless_loop() && synced_to_jack()) { - warning << _("Seamless looping cannot be supported while Ardour is using JACK transport.\n" - "Recommend changing the configured options") + warning << string_compose (_("Seamless looping cannot be supported while %1 is using JACK transport.\n" + "Recommend changing the configured options"), PROGRAM_NAME) << endmsg; return; } @@ -646,27 +679,29 @@ 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); } } } /* put the loop event into the event list */ - Event* event = new Event (Event::AutoLoop, Event::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. If doing seamless loop, force a @@ -725,16 +760,19 @@ 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; @@ -810,24 +848,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); } } } @@ -845,12 +884,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); } } } @@ -873,6 +912,8 @@ Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool 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)); + if (_transport_speed == speed) { return; } @@ -895,12 +936,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); } } } @@ -922,18 +963,14 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) /* we are stopped and we want to start rolling at speed 1 */ - if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) { - return; - } - 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); } } } @@ -946,12 +983,9 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) } else { - if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) { - return; - } - if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) { - warning << _("Global varispeed cannot be supported while Ardour is connected to JACK transport control") + warning << string_compose (_("Global varispeed cannot be supported while %1 is connected to JACK transport control"), + PROGRAM_NAME) << endmsg; return; } @@ -978,14 +1012,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; } @@ -1007,9 +1043,15 @@ Session::stop_transport (bool abort, bool clear_state) return; } - if (actively_recording() && !(transport_sub_state & StopPendingCapture) && - _worst_output_latency > current_block_size) - { + if (actively_recording() && !(transport_sub_state & StopPendingCapture) && _worst_output_latency > current_block_size) { + + 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 at the time the stop is requested, so we have to roll past that time. @@ -1019,7 +1061,7 @@ Session::stop_transport (bool abort, bool clear_state) and then we'll really be stopped. */ - Event *ev = new Event (Event::StopOnce, Event::Replace, + SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace, _transport_frame + _worst_output_latency - current_block_size, 0, 0, abort); @@ -1031,6 +1073,17 @@ Session::stop_transport (bool abort, bool clear_state) if ((transport_sub_state & PendingDeclickOut) == 0) { + + if (!(transport_sub_state & StopPendingCapture)) { + 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); + } + } + } + transport_sub_state |= PendingDeclickOut; /* we'll be called again after the declick */ pending_abort = abort; @@ -1045,6 +1098,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 @@ -1073,9 +1128,12 @@ 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); + } } deliver_mmc(MIDI::MachineControl::cmdDeferredPlay, _transport_frame); @@ -1092,7 +1150,7 @@ Session::post_transport () PostTransportWork ptw = post_transport_work (); if (ptw & PostTransportAudition) { - if (auditioner && auditioner->active()) { + if (auditioner && auditioner->auditioning()) { process_function = &Session::process_audition; } else { process_function = &Session::process_with_events; @@ -1142,25 +1200,25 @@ Session::reset_rf_scale (nframes_t motion) } void -Session::drop_sync_source () +Session::use_sync_source (Slave* new_slave) { + /* Runs in process() context */ + bool non_rt_required = false; - if (_transport_speed) { - error << _("please stop the transport before adjusting slave settings") << endmsg; - return; - } + /* XXX this deletion is problematic because we're in RT context */ delete _slave; - _slave = 0; + _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 (0); + tr->set_slaved (_slave != 0); } } @@ -1173,24 +1231,27 @@ Session::drop_sync_source () } void -Session::use_sync_source (SyncSource src) +Session::drop_sync_source () { - bool reverse = false; - bool non_rt_required = false; + request_sync_source (0); +} - if (_transport_speed) { - error << _("please stop the transport before adjusting slave settings") << endmsg; - return; - } +void +Session::switch_to_sync_source (SyncSource src) +{ + Slave* new_slave; - delete _slave; - _slave = 0; + DEBUG_TRACE (DEBUG::Slave, string_compose ("Setting up sync source %1\n", enum_2_string (src))); switch (src) { case MTC: + if (_slave && dynamic_cast(_slave)) { + return; + } + if (_mtc_port) { try { - _slave = new MTC_Slave (*this, *_mtc_port); + new_slave = new MTC_Slave (*this, *_mtc_port); } catch (failed_constructor& err) { @@ -1204,9 +1265,13 @@ Session::use_sync_source (SyncSource src) break; case MIDIClock: + if (_slave && dynamic_cast(_slave)) { + return; + } + if (_midi_clock_port) { try { - _slave = new MIDIClock_Slave (*this, *_midi_clock_port, 24); + new_slave = new MIDIClock_Slave (*this, *_midi_clock_port, 24); } catch (failed_constructor& err) { @@ -1220,44 +1285,32 @@ Session::use_sync_source (SyncSource src) break; case JACK: - _slave = new JACK_Slave (_engine.jack()); - break; - - }; - - 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)) { - non_rt_required = true; - } - (*i)->set_slaved (_slave); + if (_slave && dynamic_cast(_slave)) { + return; } - } - if (reverse) { - reverse_diskstream_buffers (); - } - - if (non_rt_required) { - add_post_transport_work (PostTransportSpeed); - _butler->schedule_transport_work (); - } + new_slave = new JACK_Slave (_engine.jack()); + break; + + default: + new_slave = 0; + break; + }; - set_dirty(); + request_sync_source (new_slave); } 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 (); @@ -1268,14 +1321,14 @@ void Session::unset_play_range () { _play_range = false; - _clear_event_type (Event::RangeStop); - _clear_event_type (Event::RangeLocate); + _clear_event_type (SessionEvent::RangeStop); + _clear_event_type (SessionEvent::RangeLocate); } void Session::set_play_range (list& range, bool leave_rolling) { - Event* ev; + SessionEvent* ev; /* Called from event-processing context */ @@ -1286,7 +1339,7 @@ Session::set_play_range (list& range, bool leave_rolling) */ if (!leave_rolling) { /* stop transport */ - Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0f, false); + SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0f, false); merge_event (ev); } return; @@ -1321,9 +1374,9 @@ Session::set_play_range (list& range, bool leave_rolling) } if (next == range.end()) { - ev = new Event (Event::RangeStop, Event::Add, requested_frame, 0, 0.0f); + ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, requested_frame, 0, 0.0f); } else { - ev = new Event (Event::RangeLocate, Event::Add, requested_frame, (*next).start, 0.0f); + ev = new SessionEvent (SessionEvent::RangeLocate, SessionEvent::Add, requested_frame, (*next).start, 0.0f); } merge_event (ev); @@ -1333,7 +1386,7 @@ Session::set_play_range (list& range, bool leave_rolling) } else if (sz == 1) { - ev = new Event (Event::RangeStop, Event::Add, range.front().end, 0, 0.0f); + ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, range.front().end, 0, 0.0f); merge_event (ev); } @@ -1344,7 +1397,7 @@ Session::set_play_range (list& range, bool leave_rolling) /* now start rolling at the right place */ - ev = new Event (Event::LocateRoll, Event::Add, Event::Immediate, range.front().start, 0.0f, false); + ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false); merge_event (ev); TransportStateChange (); @@ -1362,7 +1415,7 @@ Session::request_bounded_roll (nframes_t start, nframes_t end) void Session::request_roll_at_and_return (nframes_t start, nframes_t return_to) { - Event *ev = new Event (Event::LocateRollLocate, Event::Add, Event::Immediate, return_to, 1.0); + SessionEvent *ev = new SessionEvent (SessionEvent::LocateRollLocate, SessionEvent::Add, SessionEvent::Immediate, return_to, 1.0); ev->target2_frame = start; queue_event (ev); } @@ -1394,7 +1447,9 @@ Session::engine_halted () void Session::xrun_recovery () { - Xrun ((nframes64_t)_transport_frame); //EMIT SIGNAL + // can't cast away volatile so copy and emit that + nframes64_t tframe = _transport_frame; + Xrun (tframe); //EMIT SIGNAL if (Config->get_stop_recording_on_xrun() && actively_recording()) { @@ -1406,6 +1461,17 @@ Session::xrun_recovery () } } +void +Session::route_processors_changed (RouteProcessorChange c) +{ + if (c.type == RouteProcessorChange::MeterPointChange) { + return; + } + + update_latency_compensation (false, false); + resort_routes (); +} + void Session::update_latency_compensation (bool with_stop, bool abort) { @@ -1462,11 +1528,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 (); + } } }