SlavableAutomationControl::get_value(), if doing automation playback, should still...
[ardour.git] / libs / ardour / session_transport.cc
index e2d64dd2edd877e4983f9cfb55bfdda1b2c6ab78..4a729700abba183faf0dfbc4a0284dc1bc545688 100644 (file)
 #include "ardour/scene_changer.h"
 #include "ardour/session.h"
 #include "ardour/slave.h"
+#include "ardour/tempo.h"
 #include "ardour/operations.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -109,7 +110,7 @@ void
 Session::request_transport_speed (double speed, bool as_default)
 {
        SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed);
-       ev->third_yes_or_no = true; // as_default
+       ev->third_yes_or_no = as_default; // as_default
        DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport speed = %1 as default = %2\n", speed, as_default));
        queue_event (ev);
 }
@@ -160,9 +161,70 @@ Session::force_locate (framepos_t target_frame, bool with_roll)
        queue_event (ev);
 }
 
+void
+Session::unset_preroll_record_punch ()
+{
+       if (_preroll_record_punch_pos >= 0) {
+               remove_event (_preroll_record_punch_pos, SessionEvent::RecordStart);
+       }
+       _preroll_record_punch_pos = -1;
+}
+
+void
+Session::unset_preroll_record_trim ()
+{
+       _preroll_record_trim_len = 0;
+}
+
+void
+Session::request_preroll_record_punch (framepos_t rec_in, framecnt_t preroll)
+{
+       if (actively_recording ()) {
+               return;
+       }
+       unset_preroll_record_punch ();
+       unset_preroll_record_trim ();
+       framepos_t start = std::max ((framepos_t)0, rec_in - preroll);
+
+       _preroll_record_punch_pos = rec_in;
+       if (_preroll_record_punch_pos >= 0) {
+               replace_event (SessionEvent::RecordStart, _preroll_record_punch_pos);
+               config.set_punch_in (false);
+               config.set_punch_out (false);
+       }
+       maybe_enable_record ();
+       request_locate (start, true);
+       set_requested_return_frame (rec_in);
+}
+
+void
+Session::request_preroll_record_trim (framepos_t rec_in, framecnt_t preroll)
+{
+       if (actively_recording ()) {
+               return;
+       }
+       unset_preroll_record_punch ();
+       unset_preroll_record_trim ();
+
+       config.set_punch_in (false);
+       config.set_punch_out (false);
+
+       framepos_t pos = std::max ((framepos_t)0, rec_in - preroll);
+       _preroll_record_trim_len = preroll;
+       maybe_enable_record ();
+       request_locate (pos, true);
+       set_requested_return_frame (rec_in);
+}
+
 void
 Session::request_play_loop (bool yn, bool change_transport_roll)
 {
+       if (_slave && yn) {
+               // don't attempt to loop when not using Internal Transport
+               // see also gtk2_ardour/ardour_ui_options.cc parameter_changed()
+               return;
+       }
+
        SessionEvent* ev;
        Location *location = _locations->auto_loop_location();
        double target_speed;
@@ -367,6 +429,12 @@ Session::butler_transport_work ()
        }
 
        if (ptw & PostTransportAdjustPlaybackBuffering) {
+               /* non_realtime_locate() calls Automatable::transport_located()
+                * for every route. This eventually calls
+                * ARDOUR::AutomationList::state () which has a LocaleGuard,
+                * and would switch locales forth/back every time.
+                */
+               LocaleGuard lg;
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                        boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
                        if (tr) {
@@ -415,7 +483,7 @@ Session::butler_transport_work ()
                /* don't seek if locate will take care of that in non_realtime_stop() */
 
                if (!(ptw & PostTransportLocate)) {
-
+                       LocaleGuard lg; // see note for non_realtime_locate() above
                        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                                (*i)->non_realtime_locate (_transport_frame);
 
@@ -524,9 +592,12 @@ Session::non_realtime_locate ()
        }
 
 
-       boost::shared_ptr<RouteList> rl = routes.reader();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               (*i)->non_realtime_locate (_transport_frame);
+       {
+               LocaleGuard lg; // see note for non_realtime_locate() above
+               boost::shared_ptr<RouteList> rl = routes.reader();
+               for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+                       (*i)->non_realtime_locate (_transport_frame);
+               }
        }
 
        _scene_changer->locate (_transport_frame);
@@ -618,7 +689,7 @@ Session::select_playhead_priority_target (framepos_t& jump_to)
 bool
 Session::select_playhead_priority_target (framepos_t& jump_to)
 {
-       if (!config.get_auto_return()) {
+       if (config.get_external_sync() || !config.get_auto_return()) {
                return false;
        }
 
@@ -731,6 +802,10 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
                        flush_all_inserts ();
                }
 
+               // rg: what is the logic behind this case?
+               // _requested_return_frame should be ignored when synced_to_engine/slaved.
+               // currently worked around in MTC_Slave by forcing _requested_return_frame to -1
+               // 2016-01-10
                if ((auto_return_enabled || synced_to_engine() || _requested_return_frame >= 0) &&
                    !(ptw & PostTransportLocate)) {
 
@@ -770,6 +845,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
        }
 
        clear_clicks();
+       unset_preroll_record_trim ();
 
        /* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode.
        */
@@ -783,15 +859,18 @@ 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 */
 
-       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()));
-               (*i)->non_realtime_locate (_transport_frame);
+       {
+               LocaleGuard lg; // see note for non_realtime_locate() above
+               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()));
+                       (*i)->non_realtime_locate (_transport_frame);
 
-               if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
-                       finished = false;
-                       /* we will be back */
-                       return;
+                       if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
+                               finished = false;
+                               /* we will be back */
+                               return;
+                       }
                }
        }
 
@@ -1138,12 +1217,9 @@ 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;
 
        /* do "stopped" stuff if:
         *
@@ -1250,7 +1326,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
                                for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
                                        boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
 
-                                       if (tr && tr->record_enabled ()) {
+                                       if (tr && tr->rec_enable_control()->get_value()) {
                                                // tell it we've looped, so it can deal with the record state
                                                tr->transport_looped (_transport_frame);
                                        }
@@ -1342,7 +1418,9 @@ Session::set_transport_speed (double speed, framepos_t destination_frame, bool a
                }
 
        } else if (transport_stopped() && speed == 1.0) {
-
+               if (as_default) {
+                       _default_transport_speed = speed;
+               }
                /* we are stopped and we want to start rolling at speed 1 */
 
                if (Config->get_loop_is_mode() && play_loop) {
@@ -1561,7 +1639,7 @@ Session::start_transport ()
 
        switch (record_status()) {
        case Enabled:
-               if (!config.get_punch_in()) {
+               if (!config.get_punch_in() && !preroll_record_punch_enabled()) {
                        enable_record ();
                }
                break;
@@ -1595,6 +1673,38 @@ Session::start_transport ()
                if (!dynamic_cast<MTC_Slave*>(_slave)) {
                        send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
                }
+
+               if (actively_recording() && click_data && config.get_count_in ()) {
+                       /* calculate count-in duration (in audio samples)
+                        * - use [fixed] tempo/meter at _transport_frame
+                        * - calc duration of 1 bar + time-to-beat before or at transport_frame
+                        */
+                       const Tempo& tempo = _tempo_map->tempo_at_frame (_transport_frame);
+                       const Meter& meter = _tempo_map->meter_at_frame (_transport_frame);
+
+                       double div = meter.divisions_per_bar ();
+                       double pulses = _tempo_map->exact_qn_at_frame (_transport_frame, 0) * div / 4.0;
+                       double beats_left = fmod (pulses, div);
+
+                       _count_in_samples = meter.frames_per_bar (tempo, _current_frame_rate);
+
+                       double dt = _count_in_samples / div;
+                       if (beats_left == 0) {
+                               /* at bar boundary, count-in 2 bars before start. */
+                               _count_in_samples *= 2;
+                       } else {
+                               /* beats left after full bar until roll position */
+                               _count_in_samples += meter.frames_per_grid (tempo, _current_frame_rate) * beats_left;
+                       }
+
+                       int clickbeat = 0;
+                       framepos_t cf = _transport_frame - _count_in_samples;
+                       while (cf < _transport_frame) {
+                               add_click (cf - _worst_track_latency, clickbeat == 0);
+                               cf += dt;
+                               clickbeat = fmod (clickbeat + 1, div);
+                       }
+               }
        }
 
        DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC4 with speed = %1\n", _transport_speed));
@@ -1975,7 +2085,7 @@ Session::xrun_recovery ()
 void
 Session::route_processors_changed (RouteProcessorChange c)
 {
-       if (ignore_route_processor_changes) {
+       if (g_atomic_int_get (&_ignore_route_processor_changes) > 0) {
                return;
        }