* wrong calculation of frames_moved in Session::process_*, resulting in drift against...
authorHans Baier <hansfbaier@googlemail.com>
Sat, 10 Jan 2009 08:42:07 +0000 (08:42 +0000)
committerHans Baier <hansfbaier@googlemail.com>
Sat, 10 Jan 2009 08:42:07 +0000 (08:42 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@4397 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/audio_diskstream.h
libs/ardour/ardour/session.h
libs/ardour/session_click.cc
libs/ardour/session_process.cc
libs/ardour/session_state.cc
libs/ardour/session_transport.cc

index f918655f9800d631423444b4384c3215473e9a2f..0db44709f8267a2c4490ecd65f358c60a5e5f448 100644 (file)
@@ -150,6 +150,48 @@ class AudioDiskstream : public Diskstream
 
        XMLNode* deprecated_io_node;
 
+       /**
+        * Calculate the playback distance during varispeed playback.
+        * Important for Session::process to know exactly, how many frames
+        * were passed by.
+        */
+       static nframes_t calculate_varispeed_playback_distance(
+                       nframes_t nframes, 
+                       uint64_t& the_last_phase, 
+                       uint64_t& the_phi,
+                       uint64_t& the_target_phi)
+       {
+               // calculate playback distance in the same way 
+               // as in AudioDiskstream::process_varispeed_playback
+               uint64_t    phase = the_last_phase;
+               int64_t     phi_delta;
+               nframes_t   i = 0;
+               
+               const int64_t fractional_part_mask  = 0xFFFFFF;
+               const Sample  binary_scaling_factor = 16777216.0f;
+
+               if (the_phi != the_target_phi) {
+                       phi_delta = ((int64_t)(the_target_phi - the_phi)) / nframes;
+               } else {
+                       phi_delta = 0;
+               }
+
+               Sample fractional_phase_part;
+
+               i = 0;
+               phase = the_last_phase;
+
+               for (nframes_t outsample = 0; outsample < nframes; ++outsample) {
+                       i = phase >> 24;
+                       fractional_phase_part = (phase & fractional_part_mask) / binary_scaling_factor;
+                       phase += the_phi + phi_delta;
+               }
+
+               the_last_phase = (phase & fractional_part_mask);
+               the_phi = the_target_phi;
+               return i; // + 1;
+       }
+       
   protected:
        friend class Session;
 
index 9d12aa79b83c0d4d015db396ea0205f329504110..8b45dad958c48e61435c1c49e8fc0842d3cbf473 100644 (file)
@@ -1025,6 +1025,12 @@ class Session : public PBD::StatefulDestructible
        bool                    _silent;
        volatile double         _transport_speed;
        double                  _last_transport_speed;
+       // fixed point transport speed for varispeed playback
+       uint64_t                phi; 
+       // fixed point target transport speed for varispeed playback when tempo changes
+       uint64_t                target_phi;
+       // fixed point phase for varispeed playback
+       uint64_t                phase;
        bool                     auto_play_legal;
        nframes_t               _last_slave_transport_frame;
        nframes_t                maximum_output_latency;
index fcbf1e1b039024f776c33f0ab6e45dfc6124120c..fde698803ca6b4eaedc8fe59134adae91ed6e920 100644 (file)
@@ -53,7 +53,7 @@ Session::click (nframes_t start, nframes_t nframes, nframes_t offset)
                return;
        } 
 
-       const nframes_t end = start + (nframes_t)floor(nframes * _transport_speed);
+       const nframes_t end = start + nframes;
 
        BufferSet& bufs = get_scratch_buffers(ChanCount(DataType::AUDIO, 1));
        buf = bufs.get_audio(0).data();
index 6247a4d654dd89e10877e972fd07e30065dec28e..dfb070b61c63a1ee459a7fc2e7ab950ec792b474 100644 (file)
@@ -271,11 +271,11 @@ void
 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;
+       nframes_t      this_nframes;
+       nframes_t      end_frame;
+       nframes_t      offset;
+       bool           session_needs_butler = false;
+       nframes_t      stop_limit;
        long           frames_moved;
        
        /* make sure the auditioner is silent */
@@ -319,7 +319,17 @@ Session::process_with_events (nframes_t nframes)
                return;
        }
 
-       end_frame = _transport_frame + (nframes_t)abs(floor(nframes * _transport_speed));
+       /// 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); 
+       }
+
+       end_frame = _transport_frame + (nframes_t)frames_moved;
 
        {
                Event* this_event;
@@ -372,7 +382,6 @@ Session::process_with_events (nframes_t nframes)
                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) {
@@ -836,7 +845,15 @@ Session::process_without_events (nframes_t nframes)
 
        prepare_diskstreams ();
        
-       frames_moved = (long) floor (_transport_speed * nframes);
+       /// 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); 
+       }
 
        if (process_routes (nframes, offset)) {
                no_roll (nframes, offset);
index a3bede221a0fdc61e207df139f5a385e9ab79dac..0fcd1c95a9d464dc87c79df65f11e679cbc59354 100644 (file)
@@ -155,6 +155,8 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        insert_cnt = 0;
        _transport_speed = 0;
        _last_transport_speed = 0;
+       phi = (uint64_t) (0x1000000);
+       target_phi = phi;
        auto_play_legal = false;
        transport_sub_state = 0;
        _transport_frame = 0;
index 0649d16848a2a8e9f0d992fe388c0aba93cef8d2..5b2c351b883db6924065242a3f2689b289e484e2 100644 (file)
@@ -176,6 +176,9 @@ Session::realtime_stop (bool abort)
        reset_slave_state ();
 
        _transport_speed = 0;
+       phi = 0;
+       target_phi = 0;
+       phase = 0;
 
        if (Config->get_use_video_sync()) {
                waiting_for_sync_offset = true;
@@ -805,6 +808,8 @@ Session::set_transport_speed (double speed, bool abort)
                return;
        }
 
+       target_phi = (uint64_t) (0x1000000 * fabs(speed));
+       
        if (speed > 0) {
                speed = min (8.0, speed);
        } else if (speed < 0) {
@@ -981,7 +986,11 @@ Session::start_transport ()
        }
 
        transport_sub_state |= PendingDeclickIn;
+       
        _transport_speed = 1.0;
+       target_phi       = 0x1000000; // speed = 1
+       phi              = target_phi;
+       phase            = 0;
 
        boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
        for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {