Declick before the end of seamless loops, not after the end, so that loops are render...
[ardour.git] / libs / ardour / session_process.cc
index 33c4e5621c532f475521441df64b08e3cd1d8469..3936423b141a98e46ffc0cb09504e5bbe0bfd894 100644 (file)
 
 #include <glibmm/thread.h>
 
-#include "ardour/ardour.h"
 #include "ardour/audioengine.h"
 #include "ardour/auditioner.h"
 #include "ardour/butler.h"
+#include "ardour/cycle_timer.h"
 #include "ardour/debug.h"
+#include "ardour/graph.h"
+#include "ardour/port.h"
 #include "ardour/process_thread.h"
 #include "ardour/session.h"
 #include "ardour/slave.h"
-#include "ardour/timestamps.h"
-#include "ardour/graph.h"
-#include "ardour/audio_port.h"
+#include "ardour/ticker.h"
+#include "ardour/types.h"
 
 #include "midi++/manager.h"
 #include "midi++/mmc.h"
 
 #include "i18n.h"
 
+#include <xmmintrin.h>
+
 using namespace ARDOUR;
 using namespace PBD;
 using namespace std;
@@ -55,7 +58,7 @@ using namespace std;
 void
 Session::process (pframes_t nframes)
 {
-       MIDI::Manager::instance()->cycle_start(nframes);
+       framepos_t transport_at_start = _transport_frame;
 
        _silent = false;
 
@@ -76,17 +79,21 @@ Session::process (pframes_t nframes)
 
        _engine.main_thread()->drop_buffers ();
 
-       // the ticker is for sending time information like MidiClock
-       framepos_t transport_frames = transport_frame();
-       Timecode::BBT_Time transport_bbt;
-       bbt_time(transport_frames, transport_bbt);
-       Timecode::Time transport_timecode;
-       timecode_time(transport_frames, transport_timecode);
-       tick (transport_frames, transport_bbt, transport_timecode); /* EMIT SIGNAL */
+       /* deliver MIDI clock. Note that we need to use the transport frame
+        * position at the start of process(), not the value at the end of
+        * it. We may already have ticked() because of a transport state
+        * change, for example.
+        */
 
-       SendFeedback (); /* EMIT SIGNAL */
+       try {
+               if (!_engine.freewheeling() && Config->get_send_midi_clock() && transport_speed() == 1.0f && midi_clock->has_midi_port()) {
+                       midi_clock->tick (transport_at_start);
+               }
+       } catch (...) {
+               /* don't bother with a message */
+       }
 
-       MIDI::Manager::instance()->cycle_end();
+       SendFeedback (); /* EMIT SIGNAL */
 }
 
 int
@@ -98,19 +105,22 @@ Session::fail_roll (pframes_t nframes)
 int
 Session::no_roll (pframes_t nframes)
 {
+       PT_TIMING_CHECK (4);
+       
        framepos_t end_frame = _transport_frame + nframes; // FIXME: varispeed + no_roll ??
        int ret = 0;
-       bool declick = get_transport_declick_required();
+       int declick = get_transport_declick_required();
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        if (_click_io) {
                _click_io->silence (nframes);
        }
 
-       if (_process_graph->threads_in_use() > 0) {
+       if (_process_graph) {
                DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/no-roll\n");
                _process_graph->routes_no_roll( nframes, _transport_frame, end_frame, non_realtime_work_pending(), declick);
        } else {
+               PT_TIMING_CHECK (10);
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
 
                        if ((*i)->is_hidden()) {
@@ -125,11 +135,16 @@ Session::no_roll (pframes_t nframes)
                                break;
                        }
                }
+               PT_TIMING_CHECK (11);
        }
 
+       PT_TIMING_CHECK (5);
        return ret;
 }
 
+/** @param need_butler to be set to true by this method if it needs the butler,
+ *  otherwise it must be left alone.
+ */
 int
 Session::process_routes (pframes_t nframes, bool& need_butler)
 {
@@ -144,11 +159,7 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
        const framepos_t start_frame = _transport_frame;
        const framepos_t end_frame = _transport_frame + floor (nframes * _transport_speed);
 
-       /* XXX this is hack to force use of the graph even if we are only
-          using 1 thread. its needed because otherwise when we remove
-          tracks, the graph never gets updated.
-       */
-       if (1 || _process_graph->threads_in_use() > 0) {
+       if (_process_graph) {
                DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/process-routes\n");
                _process_graph->process_routes (nframes, start_frame, end_frame, declick, need_butler);
        } else {
@@ -163,16 +174,25 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
 
                        (*i)->set_pending_declick (declick);
 
-                       if ((ret = (*i)->roll (nframes, start_frame, end_frame, declick, need_butler)) < 0) {
+                       bool b = false;
+
+                       if ((ret = (*i)->roll (nframes, start_frame, end_frame, declick, b)) < 0) {
                                stop_transport ();
                                return -1;
                        }
+
+                       if (b) {
+                               need_butler = true;
+                       }
                }
        }
 
        return 0;
 }
 
+/** @param need_butler to be set to true by this method if it needs the butler,
+ *  otherwise it must be left alone.
+ */
 int
 Session::silent_process_routes (pframes_t nframes, bool& need_butler)
 {
@@ -181,11 +201,7 @@ Session::silent_process_routes (pframes_t nframes, bool& need_butler)
        const framepos_t start_frame = _transport_frame;
        const framepos_t end_frame = _transport_frame + lrintf(nframes * _transport_speed);
 
-       /* XXX this is hack to force use of the graph even if we are only
-          using 1 thread. its needed because otherwise when we remove
-          tracks, the graph never gets updated.
-       */
-       if (1 || _process_graph->threads_in_use() > 0) {
+       if (_process_graph) {
                _process_graph->silent_process_routes (nframes, start_frame, end_frame, need_butler);
        } else {
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
@@ -196,10 +212,16 @@ Session::silent_process_routes (pframes_t nframes, bool& need_butler)
                                continue;
                        }
 
-                       if ((ret = (*i)->silent_roll (nframes, start_frame, end_frame, need_butler)) < 0) {
+                       bool b = false;
+
+                       if ((ret = (*i)->silent_roll (nframes, start_frame, end_frame, b)) < 0) {
                                stop_transport ();
                                return -1;
                        }
+
+                       if (b) {
+                               need_butler = true;
+                       }
                }
        }
 
@@ -237,6 +259,8 @@ Session::get_track_statistics ()
 void
 Session::process_with_events (pframes_t nframes)
 {
+       PT_TIMING_CHECK (3);
+       
        SessionEvent*  ev;
        pframes_t      this_nframes;
        framepos_t     end_frame;
@@ -997,6 +1021,17 @@ Session::process_event (SessionEvent* ev)
                del = false;
                break;
 
+       case SessionEvent::AutoLoopDeclick:
+               if (play_loop) {
+                       /* Request a declick fade-out and a fade-in; the fade-out will happen
+                          at the end of the loop, and the fade-in at the start.
+                       */
+                       transport_sub_state |= (PendingLoopDeclickOut | PendingLoopDeclickIn);
+               }
+               remove = false;
+               del = false;
+               break;
+
        case SessionEvent::Locate:
                if (ev->yes_or_no) {
                        // cerr << "forced locate to " << ev->target_frame << endl;
@@ -1133,11 +1168,23 @@ Session::process_event (SessionEvent* ev)
 framepos_t
 Session::compute_stop_limit () const
 {
-       bool const punching = (config.get_punch_in () && _locations->auto_punch_location());
-
-       if (!actively_recording() && !punching && Config->get_stop_at_session_end()) {
-               return current_end_frame ();
+       if (!Config->get_stop_at_session_end ()) {
+               return max_framepos;
+       }
+       
+       bool const punching_in = (config.get_punch_in () && _locations->auto_punch_location());
+       bool const punching_out = (config.get_punch_out () && _locations->auto_punch_location());
+
+       if (actively_recording ()) {
+               /* permanently recording */
+               return max_framepos;
+       } else if (punching_in && !punching_out) {
+               /* punching in but never out */
+               return max_framepos;
+       } else if (punching_in && punching_out && _locations->auto_punch_location()->end() > current_end_frame()) {
+               /* punching in and punching out after session end */
+               return max_framepos;
        }
 
-       return max_framepos;
+       return current_end_frame ();
 }