spline interpolation: fix crash bugs on negative speed and NULL inputs
[ardour.git] / libs / ardour / session_process.cc
index 0f697806eb6fe1b18d7dcd5246209228e12a9ee4..2dd68744b4266c390e32a6e04a0791bcd5778d73 100644 (file)
@@ -58,12 +58,17 @@ Session::process (nframes_t nframes)
 
        _silent = false;
 
+       if (processing_blocked()) {
+               _silent = true;
+               return;
+       }
+
        if (non_realtime_work_pending()) {
                if (!transport_work_requested ()) {
                        post_transport ();
                } 
        } 
-       
+
        (this->*process_function) (nframes);
        
        // the ticker is for sending time information like MidiClock
@@ -89,7 +94,13 @@ Session::prepare_diskstreams ()
 }
 
 int
-Session::no_roll (nframes_t nframes, nframes_t offset)
+Session::fail_roll (nframes_t nframes)
+{
+       return no_roll (nframes);
+}
+
+int
+Session::no_roll (nframes_t nframes)
 {
        nframes_t end_frame = _transport_frame + nframes; // FIXME: varispeed + no_roll ??
        int ret = 0;
@@ -97,14 +108,7 @@ Session::no_roll (nframes_t nframes, nframes_t offset)
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        if (_click_io) {
-               _click_io->silence (nframes, offset);
-       }
-
-       if (g_atomic_int_get (&processing_prohibited)) {
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       (*i)->silence (nframes, offset);
-               }
-               return 0;
+               _click_io->silence (nframes);
        }
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
@@ -115,7 +119,7 @@ Session::no_roll (nframes_t nframes, nframes_t offset)
                
                (*i)->set_pending_declick (declick);
                
-               if ((*i)->no_roll (nframes, _transport_frame, end_frame, offset, non_realtime_work_pending(), 
+               if ((*i)->no_roll (nframes, _transport_frame, end_frame, non_realtime_work_pending(), 
                                   actively_recording(), declick)) {
                        error << string_compose(_("Session: error in no roll for %1"), (*i)->name()) << endmsg;
                        ret = -1;
@@ -127,7 +131,7 @@ Session::no_roll (nframes_t nframes, nframes_t offset)
 }
 
 int
-Session::process_routes (nframes_t nframes, nframes_t offset)
+Session::process_routes (nframes_t nframes)
 {
        bool record_active;
        int  declick = get_transport_declick_required();
@@ -154,7 +158,7 @@ Session::process_routes (nframes_t nframes, nframes_t offset)
 
                (*i)->set_pending_declick (declick);
 
-               if ((ret = (*i)->roll (nframes, start_frame, end_frame, offset, declick, record_active, rec_monitors)) < 0) {
+               if ((ret = (*i)->roll (nframes, start_frame, end_frame, declick, record_active, rec_monitors)) < 0) {
 
                        /* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
                           and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
@@ -175,7 +179,7 @@ Session::process_routes (nframes_t nframes, nframes_t offset)
 }
 
 int
-Session::silent_process_routes (nframes_t nframes, nframes_t offset)
+Session::silent_process_routes (nframes_t nframes)
 {
        bool record_active = actively_recording();
        int  declick = get_transport_declick_required();
@@ -198,7 +202,7 @@ Session::silent_process_routes (nframes_t nframes, nframes_t offset)
                        continue;
                }
 
-               if ((ret = (*i)->silent_roll (nframes, start_frame, end_frame, offset, record_active, rec_monitors)) < 0) {
+               if ((ret = (*i)->silent_roll (nframes, start_frame, end_frame, record_active, rec_monitors)) < 0) {
                        
                        /* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
                           and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
@@ -238,7 +242,7 @@ Session::commit_diskstreams (nframes_t nframes, bool &needs_butler)
                   also runs commit() for every diskstream.
                 */
 
-               if ((dret = (*i)->process (_transport_frame, nframes, 0, actively_recording(), get_rec_monitors_input())) == 0) {
+               if ((dret = (*i)->process (_transport_frame, nframes, actively_recording(), get_rec_monitors_input())) == 0) {
                        if ((*i)->commit (nframes)) {
                                needs_butler = true;
                        }
@@ -273,7 +277,6 @@ Session::process_with_events (nframes_t nframes)
        Event*         ev;
        nframes_t      this_nframes;
        nframes_t      end_frame;
-       nframes_t      offset;
        bool           session_needs_butler = false;
        nframes_t      stop_limit;
        long           frames_moved;
@@ -281,7 +284,7 @@ Session::process_with_events (nframes_t nframes)
        /* make sure the auditioner is silent */
 
        if (auditioner) {
-               auditioner->silence (nframes, 0);
+               auditioner->silence (nframes);
        }
 
        /* handle any pending events */
@@ -319,14 +322,12 @@ Session::process_with_events (nframes_t nframes)
                return;
        }
 
-       /// TODO: Figure out what happens to phi and phase, if transport speed momentarily becomes
-       /// 1.0 eg. during the adjustments of a slave. If that is a bug, then AudioDiskstream::process
-       /// is very likely broken too
        if (_transport_speed == 1.0) {
                frames_moved = (long) nframes;
        } else {                
-               frames_moved = (long) AudioDiskstream::
-                       calculate_varispeed_playback_distance(nframes, phase, phi, target_phi); 
+               interpolation.set_target_speed (fabs(_target_transport_speed));
+               interpolation.set_speed (fabs(_transport_speed));
+               frames_moved = (long) interpolation.interpolate (0, nframes, 0, 0);
        }
 
        end_frame = _transport_frame + (nframes_t)frames_moved;
@@ -341,13 +342,13 @@ Session::process_with_events (nframes_t nframes)
                }
                
                if (!_exporting && _slave) {
-                       if (!follow_slave (nframes, 0)) {
+                       if (!follow_slave (nframes)) {
                                return;
                        }
                } 
 
                if (_transport_speed == 0) {
-                       no_roll (nframes, 0);
+                       no_roll (nframes);
                        return;
                }
        
@@ -367,7 +368,7 @@ Session::process_with_events (nframes_t nframes)
                }
 
                if (maybe_stop (stop_limit)) {
-                       no_roll (nframes, 0);
+                       no_roll (nframes);
                        return;
                } 
 
@@ -375,13 +376,14 @@ Session::process_with_events (nframes_t nframes)
                the_next_one = next_event;
                ++the_next_one;
 
-               offset = 0;
-
                /* yes folks, here it is, the actual loop where we really truly
-                  process some audio */
+                  process some audio 
+               */
+
                while (nframes) {
 
                        this_nframes = nframes; /* real (jack) time relative */
+                       frames_moved = (long) floor (_transport_speed * nframes); /* transport relative */
 
                        /* running an event, position transport precisely to its time */
                        if (this_event && this_event->action_frame <= end_frame && this_event->action_frame >= _transport_frame) {
@@ -392,20 +394,19 @@ Session::process_with_events (nframes_t nframes)
 
                        if (this_nframes) {
                                
-                               click (_transport_frame, nframes, offset);
+                               click (_transport_frame, this_nframes);
                                
                                /* now process frames between now and the first event in this block */
                                prepare_diskstreams ();
 
-                               if (process_routes (this_nframes, offset)) {
-                                       no_roll (nframes, 0);
+                               if (process_routes (this_nframes)) {
+                                       fail_roll (nframes);
                                        return;
                                }
                                
                                commit_diskstreams (this_nframes, session_needs_butler);
 
                                nframes -= this_nframes;
-                               offset += this_nframes;
                                
                                if (frames_moved < 0) {
                                        decrement_transport_position (-frames_moved);
@@ -417,6 +418,8 @@ Session::process_with_events (nframes_t nframes)
                                check_declick_out ();
                        }
 
+                       _engine.split_cycle (this_nframes);
+                       
                        /* now handle this event and all others scheduled for the same time */
                        
                        while (this_event && this_event->action_frame == _transport_frame) {
@@ -432,8 +435,8 @@ Session::process_with_events (nframes_t nframes)
 
                        /* if an event left our state changing, do the right thing */
 
-                       if (non_realtime_work_pending()) {
-                               no_roll (nframes, offset);
+                       if (nframes && non_realtime_work_pending()) {
+                               no_roll (nframes);
                                break;
                        }
 
@@ -472,7 +475,7 @@ Session::transport_locked () const
 }
 
 bool
-Session::follow_slave (nframes_t nframes, nframes_t offset)
+Session::follow_slave (nframes_t nframes)
 {
        double slave_speed;
        nframes_t slave_transport_frame;
@@ -504,7 +507,7 @@ Session::follow_slave (nframes_t nframes, nframes_t offset)
                slave_speed = 0.0f;
        }
 
-       if (_slave->is_always_synced() || Config->get_timecode_source_is_synced()) {
+       if (_slave->is_always_synced() || config.get_timecode_source_is_synced()) {
 
                /* if the TC source is synced, then we assume that its 
                   speed is binary: 0.0 or 1.0
@@ -526,7 +529,7 @@ Session::follow_slave (nframes_t nframes, nframes_t offset)
        
        track_slave_state(slave_speed, slave_transport_frame, this_delta, starting);
 
-       if (slave_state == Running && !_slave->is_always_synced() && !Config->get_timecode_source_is_synced()) {
+       if (slave_state == Running && !_slave->is_always_synced() && !config.get_timecode_source_is_synced()) {
 
                if (_transport_speed != 0.0f) {
                        
@@ -592,14 +595,14 @@ Session::follow_slave (nframes_t nframes, nframes_t offset)
        cerr << "reached silent_motion:" <<endl;
        #endif
        
-       follow_slave_silently(nframes, offset, slave_speed);
+       follow_slave_silently (nframes, slave_speed);
        
   noroll:
        /* don't move at all */
        #ifdef DEBUG_SLAVES     
        cerr << "reached no_roll:" <<endl;
        #endif
-       no_roll (nframes, 0);
+       no_roll (nframes);
        return false;
 }
 
@@ -748,7 +751,7 @@ Session::track_slave_state(
 }
 
 void
-Session::follow_slave_silently(nframes_t nframes, nframes_t offset, float slave_speed)
+Session::follow_slave_silently (nframes_t nframes, float slave_speed)
 {
        if (slave_speed && _transport_speed) {
 
@@ -759,7 +762,7 @@ Session::follow_slave_silently(nframes_t nframes, nframes_t offset, float slave_
                bool need_butler;
                
                prepare_diskstreams ();
-               silent_process_routes (nframes, offset);
+               silent_process_routes (nframes);
                commit_diskstreams (nframes, need_butler);
 
                if (need_butler) {
@@ -796,7 +799,6 @@ Session::process_without_events (nframes_t nframes)
        bool session_needs_butler = false;
        nframes_t stop_limit;
        long frames_moved;
-       nframes_t offset = 0;
 
        if (!process_can_proceed()) {
                _silent = true;
@@ -804,13 +806,13 @@ Session::process_without_events (nframes_t nframes)
        }
 
        if (!_exporting && _slave) {
-               if (!follow_slave (nframes, 0)) {
+               if (!follow_slave (nframes)) {
                        return;
                }
        } 
 
        if (_transport_speed == 0) {
-               no_roll (nframes, 0);
+               fail_roll (nframes);
                return;
        }
                
@@ -829,30 +831,28 @@ Session::process_without_events (nframes_t nframes)
        }
                
        if (maybe_stop (stop_limit)) {
-               no_roll (nframes, 0);
+               fail_roll (nframes);
                return;
        } 
 
-       if (maybe_sync_start (nframes, offset)) {
+       if (maybe_sync_start (nframes)) {
                return;
        }
 
-       click (_transport_frame, nframes, offset);
+       click (_transport_frame, nframes);
 
        prepare_diskstreams ();
        
-       /// TODO: Figure out what happens to phi and phase, if transport speed momentarily becomes
-       /// 1.0 eg. during the adjustments of a slave. If that is a bug, then AudioDiskstream::process
-       /// is very likely broken too
        if (_transport_speed == 1.0) {
                frames_moved = (long) nframes;
        } else {                
-               frames_moved = (long) AudioDiskstream::
-                       calculate_varispeed_playback_distance(nframes, phase, phi, target_phi); 
+               interpolation.set_target_speed (fabs(_target_transport_speed));
+               interpolation.set_speed (fabs(_transport_speed));
+               frames_moved = (long) interpolation.interpolate (0, nframes, 0, 0);
        }
 
-       if (process_routes (nframes, offset)) {
-               no_roll (nframes, offset);
+       if (process_routes (nframes)) {
+               fail_roll (nframes);
                return;
        }
 
@@ -882,7 +882,7 @@ Session::process_audition (nframes_t nframes)
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (!(*i)->is_hidden()) {
-                       (*i)->silence (nframes, 0);
+                       (*i)->silence (nframes);
                }
        }
 
@@ -916,7 +916,7 @@ Session::process_audition (nframes_t nframes)
 }
 
 bool
-Session::maybe_sync_start (nframes_t& nframes, nframes_t& offset)
+Session::maybe_sync_start (nframes_t& nframes)
 {
        nframes_t sync_offset;
 
@@ -931,9 +931,9 @@ Session::maybe_sync_start (nframes_t& nframes, nframes_t& offset)
                   is left to do.
                */
 
-               no_roll (sync_offset, 0);
+               no_roll (sync_offset);
                nframes -= sync_offset;
-               offset += sync_offset;
+               Port::increment_port_offset (sync_offset);
                waiting_for_sync_offset = false;
                
                if (nframes == 0) {
@@ -947,9 +947,7 @@ Session::maybe_sync_start (nframes_t& nframes, nframes_t& offset)
                   with any fancy stuff here, just the minimal silence.
                */
 
-               g_atomic_int_inc (&processing_prohibited);
-               no_roll (nframes, 0);
-               g_atomic_int_dec_and_test (&processing_prohibited);
+               _silent = true;
 
                if (Config->get_locate_while_waiting_for_sync()) {
                        if (micro_locate (nframes)) {