X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_transport.cc;h=48b84c492c84ff50ffc1ecc65f3d8538a58cfdc5;hb=de9e216cb5fcc08b6c39071d7cef031673aa67da;hp=1d5f72e670b610adc565b4a6532d922e7d172652;hpb=6c76a275e17cb648a0722039a2c77a7f05361e22;p=ardour.git diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 1d5f72e670..48b84c492c 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -21,6 +21,10 @@ #include #include +#ifdef WAF_BUILD +#include "libardour-config.h" +#endif + #include "pbd/undo.h" #include "pbd/error.h" @@ -40,6 +44,7 @@ #include "ardour/location.h" #include "ardour/session.h" #include "ardour/slave.h" +#include "ardour/operations.h" #include "i18n.h" @@ -106,6 +111,20 @@ Session::request_transport_speed (double speed) 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) { @@ -123,7 +142,7 @@ Session::request_stop (bool abort, bool clear_state) } void -Session::request_locate (nframes_t target_frame, bool with_roll) +Session::request_locate (framepos_t target_frame, bool with_roll) { 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)); @@ -188,6 +207,14 @@ Session::realtime_stop (bool abort, bool clear_state) todo = PostTransportWork (todo | PostTransportStop); } + /* call routes */ + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin (); i != r->end(); ++i) { + (*i)->realtime_handle_transport_stopped (); + } + if (actively_recording()) { /* move the transport position back to where the @@ -195,14 +222,14 @@ Session::realtime_stop (bool abort, bool clear_state) 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 */ @@ -225,9 +252,9 @@ Session::realtime_stop (bool abort, bool clear_state) _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; @@ -243,6 +270,15 @@ Session::realtime_stop (bool abort, bool clear_state) transport_sub_state = 0; } +void +Session::realtime_locate () +{ + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->realtime_locate (); + } +} + void Session::butler_transport_work () { @@ -257,26 +293,26 @@ 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 tr = boost::dynamic_pointer_cast (*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 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) { @@ -427,17 +463,17 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) 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 tr = boost::dynamic_pointer_cast (*i); @@ -446,9 +482,9 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } } - 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 r = routes.reader (); @@ -463,7 +499,11 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } 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 = @@ -481,7 +521,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; @@ -569,8 +609,11 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) 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 (!dynamic_cast(_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 */ @@ -578,9 +621,9 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) 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 */ @@ -657,8 +700,9 @@ Session::set_play_loop (bool yn) } 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; } @@ -751,7 +795,7 @@ Session::start_locate (framepos_t target_frame, bool with_roll, bool with_flush, } int -Session::micro_locate (nframes_t distance) +Session::micro_locate (framecnt_t distance) { boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { @@ -789,14 +833,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool 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)) { @@ -808,8 +845,27 @@ 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; + 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) { @@ -842,28 +898,12 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool if (with_roll) { /* switch from input if we're going to roll */ if (Config->get_monitoring_model() == HardwareMonitoring) { - - 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; - 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 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; - tr->monitor_input (true); - } - } + set_track_monitor_input_status (true); } } @@ -905,8 +945,8 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool } /** 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) @@ -933,16 +973,8 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) /* we are rolling and we want to stop */ - if (Config->get_monitoring_model() == HardwareMonitoring) - { - 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; - tr->monitor_input (true); - } - } + if (Config->get_monitoring_model() == HardwareMonitoring) { + set_track_monitor_input_status (true); } if (synced_to_jack ()) { @@ -957,21 +989,15 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) } else { 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 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; - tr->monitor_input (false); - } - } + if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { + set_track_monitor_input_status (false); } if (synced_to_jack()) { @@ -981,10 +1007,13 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) } } 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; } @@ -1022,7 +1051,6 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); if (tr && tr->realtime_set_speed (tr->speed(), true)) { todo = PostTransportWork (todo | PostTransportSpeed); - break; } } @@ -1030,6 +1058,8 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state) add_post_transport_work (todo); _butler->schedule_transport_work (); } + + TransportStateChange (); /* EMIT SIGNAL */ } } @@ -1042,7 +1072,7 @@ 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_input_latency() > current_block_size) { boost::shared_ptr rl = routes.reader(); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { @@ -1050,7 +1080,7 @@ Session::stop_transport (bool abort, bool clear_state) 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. @@ -1061,8 +1091,8 @@ Session::stop_transport (bool abort, bool clear_state) */ 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; @@ -1070,19 +1100,18 @@ Session::stop_transport (bool abort, bool clear_state) return; } - if ((transport_sub_state & PendingDeclickOut) == 0) { - if (!(transport_sub_state & StopPendingCapture)) { + 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; @@ -1093,9 +1122,12 @@ Session::stop_transport (bool abort, bool clear_state) _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; @@ -1133,12 +1165,14 @@ Session::start_transport () 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 (!dynamic_cast(_slave)) { + MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay)); + } TransportStateChange (); /* EMIT SIGNAL */ } @@ -1182,7 +1216,7 @@ Session::post_transport () } void -Session::reset_rf_scale (nframes_t motion) +Session::reset_rf_scale (framecnt_t motion) { cumulative_rf_motion += motion; @@ -1279,9 +1313,9 @@ Session::switch_to_sync_source (SyncSource src) 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; @@ -1359,7 +1393,7 @@ Session::set_play_range (list& range, bool leave_rolling) /* locating/stopping is subject to delays for declicking. */ - nframes_t requested_frame = (*i).end; + framepos_t requested_frame = i->end; if (requested_frame > current_block_size) { requested_frame -= current_block_size; @@ -1398,7 +1432,7 @@ Session::set_play_range (list& range, bool leave_rolling) } void -Session::request_bounded_roll (nframes_t start, nframes_t end) +Session::request_bounded_roll (framepos_t start, framepos_t end) { AudioRange ar (start, end, 0); list lar; @@ -1407,7 +1441,7 @@ Session::request_bounded_roll (nframes_t start, nframes_t end) request_play_range (&lar, true); } void -Session::request_roll_at_and_return (nframes_t start, nframes_t return_to) +Session::request_roll_at_and_return (framepos_t start, framepos_t return_to) { SessionEvent *ev = new SessionEvent (SessionEvent::LocateRollLocate, SessionEvent::Add, SessionEvent::Immediate, return_to, 1.0); ev->target2_frame = start; @@ -1441,7 +1475,7 @@ Session::engine_halted () void Session::xrun_recovery () { - Xrun (_transport_frame); //EMIT SIGNAL + Xrun (_transport_frame); /* EMIT SIGNAL */ if (Config->get_stop_recording_on_xrun() && actively_recording()) { @@ -1460,74 +1494,10 @@ Session::route_processors_changed (RouteProcessorChange c) return; } - update_latency_compensation (false, false); + update_latency_compensation (); resort_routes (); -} - -void -Session::update_latency_compensation (bool with_stop, bool abort) -{ - bool update_jack = false; - PostTransportWork ptw; - - if (_state_of_the_state & Deletion) { - return; - } - - _worst_track_latency = 0; - ptw = post_transport_work(); -#undef DEBUG_LATENCY -#ifdef DEBUG_LATENCY - cerr << "\n---------------------------------\nUPDATE LATENCY\n"; -#endif - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - - if (with_stop) { - (*i)->handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush)); - } - - nframes_t old_latency = (*i)->output()->signal_latency (); - nframes_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 (); - - /* reflect any changes in latencies into capture offsets - */ - - 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 (); - } - } + set_dirty (); } void @@ -1547,17 +1517,17 @@ Session::reset_jack_connection (jack_client_t* jack) } bool -Session::maybe_stop (nframes_t limit) +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