Lua: Lock bindings into memory for rt-scripts
[ardour.git] / libs / ardour / session.cc
index f6f22ea1e36b91c876fc9fb13b77cd94581f2efa..6d2ca75719a9c98b91b45b61763f92fead8ed4c1 100644 (file)
@@ -93,6 +93,7 @@
 #include "ardour/revision.h"
 #include "ardour/route_graph.h"
 #include "ardour/route_group.h"
+#include "ardour/rt_tasklist.h"
 #include "ardour/send.h"
 #include "ardour/selection.h"
 #include "ardour/session.h"
@@ -186,6 +187,8 @@ Session::Session (AudioEngine &eng,
        , _session_range_end_is_free (true)
        , _slave (0)
        , _silent (false)
+       , _remaining_latency_preroll (0)
+       , _engine_speed (1.0)
        , _transport_speed (0)
        , _default_transport_speed (1.0)
        , _last_transport_speed (0)
@@ -193,14 +196,12 @@ Session::Session (AudioEngine &eng,
        , _target_transport_speed (0.0)
        , auto_play_legal (false)
        , _last_slave_transport_sample (0)
-       , maximum_output_latency (0)
        , _requested_return_sample (-1)
        , current_block_size (0)
        , _worst_output_latency (0)
        , _worst_input_latency (0)
-       , _worst_track_latency (0)
-       , _worst_track_out_latency (0)
-       , _worst_track_roll_delay (0)
+       , _worst_route_latency (0)
+       , _send_latency_changes (0)
        , _have_captured (false)
        , _non_soloed_outs_muted (false)
        , _listening (false)
@@ -224,7 +225,6 @@ Session::Session (AudioEngine &eng,
        , _realtime_export (false)
        , _region_export (false)
        , _export_preroll (0)
-       , _export_latency (0)
        , _pre_export_mmc_enabled (false)
        , _name (snapshot_name)
        , _is_new (true)
@@ -313,7 +313,6 @@ Session::Session (AudioEngine &eng,
        , _play_range (false)
        , _range_selection (-1,-1)
        , _object_selection (-1,-1)
-       , _preroll_record_punch_pos (-1)
        , _preroll_record_trim_len (0)
        , _count_in_once (false)
        , main_outs (0)
@@ -464,6 +463,8 @@ Session::Session (AudioEngine &eng,
        StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1));
        EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
 
+       Send::ChangedLatency.connect_same_thread (*this, boost::bind (&Session::send_latency_compensation_change, this));
+
        emit_thread_start ();
        auto_connect_thread_start ();
 
@@ -600,6 +601,8 @@ Session::immediately_post_engine ()
         * session or set state for an existing one.
         */
 
+       _rt_tasklist.reset (new RTTaskList ());
+
        if (how_many_dsp_threads () > 1) {
                /* For now, only create the graph if we are using >1 DSP threads, as
                   it is a bit slower than the old code with 1 thread.
@@ -851,7 +854,6 @@ Session::destroy ()
                        case SessionEvent::Skip:
                        case SessionEvent::PunchIn:
                        case SessionEvent::PunchOut:
-                       case SessionEvent::RecordStart:
                        case SessionEvent::StopOnce:
                        case SessionEvent::RangeStop:
                        case SessionEvent::RangeLocate:
@@ -1657,7 +1659,7 @@ Session::auto_punch_start_changed (Location* location)
 {
        replace_event (SessionEvent::PunchIn, location->start());
 
-       if (get_record_enabled() && config.get_punch_in()) {
+       if (get_record_enabled() && config.get_punch_in() && !actively_recording ()) {
                /* capture start has been changed, so save new pending state */
                save_state ("", true);
        }
@@ -1666,19 +1668,14 @@ Session::auto_punch_start_changed (Location* location)
 void
 Session::auto_punch_end_changed (Location* location)
 {
-       samplepos_t when_to_stop = location->end();
-       // when_to_stop += _worst_output_latency + _worst_input_latency;
-       replace_event (SessionEvent::PunchOut, when_to_stop);
+       replace_event (SessionEvent::PunchOut, location->end());
 }
 
 void
 Session::auto_punch_changed (Location* location)
 {
-       samplepos_t when_to_stop = location->end();
-
-       replace_event (SessionEvent::PunchIn, location->start());
-       //when_to_stop += _worst_output_latency + _worst_input_latency;
-       replace_event (SessionEvent::PunchOut, when_to_stop);
+       auto_punch_start_changed (location);
+       auto_punch_end_changed (location);
 }
 
 /** @param loc A loop location.
@@ -1756,7 +1753,7 @@ Session::set_auto_punch_location (Location* location)
        if ((existing = _locations->auto_punch_location()) != 0 && existing != location) {
                punch_connections.drop_connections();
                existing->set_auto_punch (false, this);
-               remove_event (existing->start(), SessionEvent::PunchIn);
+               clear_events (SessionEvent::PunchIn);
                clear_events (SessionEvent::PunchOut);
                auto_punch_location_changed (0);
        }
@@ -2120,7 +2117,6 @@ Session::disable_record (bool rt_context, bool force)
                if (!rt_context) {
                        remove_pending_capture_state ();
                }
-               unset_preroll_record_punch ();
        }
 }
 
@@ -2158,7 +2154,7 @@ Session::maybe_enable_record (bool rt_context)
        }
 
        if (_transport_speed) {
-               if (!config.get_punch_in() && !preroll_record_punch_enabled ()) {
+               if (!config.get_punch_in()) {
                        enable_record ();
                }
        } else {
@@ -2172,14 +2168,12 @@ Session::maybe_enable_record (bool rt_context)
 samplepos_t
 Session::audible_sample (bool* latent_locate) const
 {
-       samplepos_t ret;
-
-       sampleoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start ();
-       offset *= transport_speed ();
        if (latent_locate) {
                *latent_locate = false;
        }
 
+       samplepos_t ret;
+
        if (synced_to_engine()) {
                /* Note: this is basically just sync-to-JACK */
                ret = _engine.transport_sample();
@@ -2187,57 +2181,37 @@ Session::audible_sample (bool* latent_locate) const
                ret = _transport_sample;
        }
 
-       if (transport_rolling()) {
-               ret -= offset;
+       assert (ret >= 0);
 
-               /* Check to see if we have passed the first guaranteed
-                * audible sample past our last start position. if not,
-                * return that last start point because in terms
-                * of audible samples, we have not moved yet.
-                *
-                * `Start position' in this context means the time we last
-                * either started, located, or changed transport direction.
-                */
-
-               if (_transport_speed > 0.0f) {
+       if (!transport_rolling()) {
+               return ret;
+       }
 
-                       if (!play_loop || !have_looped) {
-                               if (ret < _last_roll_or_reversal_location) {
-                                       if (latent_locate) {
-                                               *latent_locate = true;
-                                       }
-                                       return _last_roll_or_reversal_location;
-                               }
-                       } else {
-                               /* the play-position wrapped at the loop-point
-                                * ardour is already playing the beginning of the loop,
-                                * but due to playback latency, the "audible frame"
-                                * is still at the end of the loop.
-                                */
-                               Location *location = _locations->auto_loop_location();
-                               sampleoffset_t lo = location->start() - ret;
-                               if (lo > 0) {
-                                       ret = location->end () - lo;
-                                       if (latent_locate) {
-                                               *latent_locate = true;
-                                       }
+#if 0 // TODO looping
+       if (_transport_speed > 0.0f) {
+               if (play_loop && have_looped) {
+                       /* the play-position wrapped at the loop-point
+                        * ardour is already playing the beginning of the loop,
+                        * but due to playback latency, the "audible frame"
+                        * is still at the end of the loop.
+                        */
+                       Location *location = _locations->auto_loop_location();
+                       sampleoffset_t lo = location->start() - ret;
+                       if (lo > 0) {
+                               ret = location->end () - lo;
+                               if (latent_locate) {
+                                       *latent_locate = true;
                                }
                        }
-
-               } else if (_transport_speed < 0.0f) {
-
-                       /* XXX wot? no backward looping? */
-
-                       if (ret > _last_roll_or_reversal_location) {
-                               return _last_roll_or_reversal_location;
-                       }
                }
+       } else if (_transport_speed < 0.0f) {
+               /* XXX wot? no backward looping? */
        }
+#endif
 
        return std::max ((samplepos_t)0, ret);
 }
 
-
 samplecnt_t
 Session::preroll_samples (samplepos_t pos) const
 {
@@ -3438,7 +3412,7 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                        }
 
                        /* set this name in the XML description that we are about to use */
-
+#warning fixme -- no more Diskstream
                        if (pd == CopyPlaylist) {
                                XMLNode* ds_node = find_named_node (node_copy, "Diskstream");
                                if (ds_node) {
@@ -3582,8 +3556,8 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output
 
        graph_reordered ();
 
-       update_latency (true);
        update_latency (false);
+       update_latency (true);
 
        set_dirty();
 
@@ -5522,7 +5496,6 @@ Session::setup_lua ()
 #ifndef NDEBUG
        lua.Print.connect (&_lua_print);
 #endif
-       lua.tweak_rt_gc ();
        lua.sandbox (true);
        lua.do_command (
                        "function ArdourSession ()"
@@ -5653,9 +5626,11 @@ Session::setup_lua ()
                abort(); /*NOTREACHED*/
        }
 
+       lua_mlock (L, 1);
        LuaBindings::stddef (L);
        LuaBindings::common (L);
        LuaBindings::dsp (L);
+       lua_mlock (L, 0);
        luabridge::push <Session *> (L, this);
        lua_setglobal (L, "Session");
 }
@@ -5740,16 +5715,9 @@ Session::graph_reordered ()
        resort_routes ();
 
        /* force all diskstreams to update their capture offset values to
-          reflect any changes in latencies within the graph.
-       */
-
-       boost::shared_ptr<RouteList> rl = routes.reader ();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr) {
-                       tr->update_latency_information ();
-               }
-       }
+        * reflect any changes in latencies within the graph.
+        */
+       update_route_latency (false, true);
 }
 
 /** @return Number of samples that there is disk space available to write,
@@ -6859,129 +6827,132 @@ Session::unknown_processors () const
 }
 
 void
-Session::update_latency (bool playback)
+Session::set_worst_io_latencies_x (IOChange, void *)
 {
+               set_worst_io_latencies ();
+}
 
-       DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
-
-       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
-               return;
-       }
-       if (!_engine.running()) {
-               return;
-       }
+void
+Session::send_latency_compensation_change ()
+{
+       /* As a result of Send::set_output_latency()
+        * or InternalReturn::set_playback_offset ()
+        * the send's own latency can change (source track
+        * is aligned with target bus).
+        *
+        * This can only happen be triggered by
+        * Route::update_signal_latency ()
+        * when updating the processor latency.
+        *
+        * We need to walk the graph again to take those changes into account
+        * (we should probably recurse or process the graph in a 2 step process).
+        */
+       ++_send_latency_changes;
+}
 
+bool
+Session::update_route_latency (bool playback, bool apply_to_delayline)
+{
+       /* Note: RouteList is process-graph sorted */
        boost::shared_ptr<RouteList> r = routes.reader ();
-       samplecnt_t max_latency = 0;
 
        if (playback) {
-               /* reverse the list so that we work backwards from the last route to run to the first */
-                RouteList* rl = routes.reader().get();
-                r.reset (new RouteList (*rl));
+               /* reverse the list so that we work backwards from the last route to run to the first,
+                * this is not needed, but can help to reduce the iterations for aux-sends.
+                */
+               RouteList* rl = routes.reader().get();
+               r.reset (new RouteList (*rl));
                reverse (r->begin(), r->end());
        }
 
-       /* compute actual latency values for the given direction and store them all in per-port
-          structures. this will also publish the same values (to JACK) so that computation of latency
-          for routes can consistently use public latency values.
-       */
+       bool changed = false;
+       int bailout = 0;
+restart:
+       _send_latency_changes = 0;
+       _worst_route_latency = 0;
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               max_latency = max (max_latency, (*i)->set_private_port_latencies (playback));
+               // if (!(*i)->active()) { continue ; } // TODO
+               samplecnt_t l;
+               if ((*i)->signal_latency () != (l = (*i)->update_signal_latency (apply_to_delayline))) {
+                       changed = true;
+               }
+               _worst_route_latency = std::max (l, _worst_route_latency);
        }
 
-        /* because we latency compensate playback, our published playback latencies should
-           be the same for all output ports - all material played back by ardour has
-           the same latency, whether its caused by plugins or by latency compensation. since
-           these may differ from the values computed above, reset all playback port latencies
-           to the same value.
-        */
-
-        DEBUG_TRACE (DEBUG::Latency, string_compose ("Set public port latencies to %1\n", max_latency));
-
-        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                (*i)->set_public_port_latencies (max_latency, playback);
-        }
-
-       if (playback) {
-
-               post_playback_latency ();
-
-       } else {
-
-               post_capture_latency ();
+       if (_send_latency_changes > 0) {
+               // only 1 extra iteration is needed (we allow only 1 level of aux-sends)
+               // BUT..  jack'n'sends'n'bugs
+               if (++bailout < 5) {
+                       cerr << "restarting Session::update_latency. # of send changes: " << _send_latency_changes << " iteration: " << bailout << endl;
+                       goto restart;
+               }
        }
 
-       DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n");
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_route_latency, (changed ? "yes" : "no")));
+
+       return changed;
 }
 
 void
-Session::post_playback_latency ()
+Session::update_latency (bool playback)
 {
-       set_worst_playback_latency ();
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
 
-       boost::shared_ptr<RouteList> r = routes.reader ();
+       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
+               return;
+       }
+       if (!_engine.running()) {
+               return;
+       }
 
-       _worst_track_out_latency = 0;
+       /* Note; RouteList is sorted as process-graph */
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               assert (!(*i)->is_auditioner()); // XXX remove me
-               if (!(*i)->active()) { continue ; }
-               _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
-               if (boost::dynamic_pointer_cast<Track> (*i)) {
-                       _worst_track_out_latency = max (_worst_track_out_latency, (*i)->output ()->latency ());
-               }
+       if (playback) {
+               /* reverse the list so that we work backwards from the last route to run to the first */
+               RouteList* rl = routes.reader().get();
+               r.reset (new RouteList (*rl));
+               reverse (r->begin(), r->end());
        }
 
-       _worst_track_roll_delay = 0;
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->active()) { continue ; }
-               (*i)->set_latency_compensation (_worst_track_latency + _worst_track_out_latency - (*i)->output ()->latency ());
-               if (boost::dynamic_pointer_cast<Track> (*i)) {
-                       _worst_track_roll_delay = max (_worst_track_roll_delay, (*i)->initial_delay ());
-               }
+               samplecnt_t latency = (*i)->set_private_port_latencies (playback);
+               (*i)->set_public_port_latencies (latency, playback);
        }
-}
 
-void
-Session::post_capture_latency ()
-{
-       set_worst_capture_latency ();
-
-       /* reflect any changes in capture latencies into capture offsets
-        */
-
-       boost::shared_ptr<RouteList> rl = routes.reader();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr) {
-                       tr->update_latency_information ();
-               }
+       if (playback) {
+               set_worst_output_latency ();
+               update_route_latency (true, true);
+       } else {
+               set_worst_input_latency ();
+               update_route_latency (false, false);
        }
+
+       DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n");
 }
 
 void
 Session::initialize_latencies ()
 {
-        {
-                Glib::Threads::Mutex::Lock lm (_engine.process_lock());
-                update_latency (false);
-                update_latency (true);
-        }
+       {
+               Glib::Threads::Mutex::Lock lm (_engine.process_lock());
+               update_latency (false);
+               update_latency (true);
+       }
 
-        set_worst_io_latencies ();
+       set_worst_io_latencies ();
 }
 
 void
 Session::set_worst_io_latencies ()
 {
-       set_worst_playback_latency ();
-       set_worst_capture_latency ();
+       set_worst_output_latency ();
+       set_worst_input_latency ();
 }
 
 void
-Session::set_worst_playback_latency ()
+Session::set_worst_output_latency ()
 {
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                return;
@@ -6999,11 +6970,13 @@ Session::set_worst_playback_latency ()
                _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency());
        }
 
+       _worst_output_latency = max (_worst_output_latency, _click_io->latency());
+
        DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst output latency: %1\n", _worst_output_latency));
 }
 
 void
-Session::set_worst_capture_latency ()
+Session::set_worst_input_latency ()
 {
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                return;
@@ -7021,51 +6994,29 @@ Session::set_worst_capture_latency ()
                _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency());
        }
 
-        DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
 }
 
 void
 Session::update_latency_compensation (bool force_whole_graph)
 {
-       bool some_track_latency_changed = false;
-
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                return;
        }
 
-       DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency compensation\n\n");
-
-       _worst_track_latency = 0;
-
-       boost::shared_ptr<RouteList> r = routes.reader ();
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               assert (!(*i)->is_auditioner()); // XXX remove me
-               if ((*i)->active()) {
-                       samplecnt_t tl;
-                       if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) {
-                               some_track_latency_changed = true;
-                       }
-                       _worst_track_latency = max (tl, _worst_track_latency);
-               }
-       }
-
-       DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_track_latency,
-                                                    (some_track_latency_changed ? "yes" : "no")));
-
-       DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n");
+       bool some_track_latency_changed = update_route_latency (false, false);
 
        if (some_track_latency_changed || force_whole_graph)  {
                _engine.update_latencies ();
-       }
-
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (!tr) {
-                       continue;
+               /* above call will ask the backend up update its latencies, which
+                * eventually will trigger  AudioEngine::latency_callback () and
+                * call Session::update_latency ()
+                */
+       } else {
+               boost::shared_ptr<RouteList> r = routes.reader ();
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                       (*i)->apply_latency_compensation ();
                }
-               tr->update_latency_information ();
        }
 }
 
@@ -7398,12 +7349,12 @@ Session::auto_connect_thread_run ()
                        /* this is only used for updating plugin latencies, the
                         * graph does not change. so it's safe in general.
                         * BUT..
-                        * .. update_latency_compensation () entails Track::update_latency_information()
-                        * which calls DiskWriter::set_capture_offset () which
-                        * modifies the capture offset... which can be a proplem
-                        * in "prepare_to_stop"
+                        * update_latency_compensation ()
+                        * calls DiskWriter::set_capture_offset () which
+                        * modifies the capture-offset, which can be a problem.
                         */
                        while (g_atomic_int_and (&_latency_recompute_pending, 0)) {
+                               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                update_latency_compensation ();
                        }
                }