Modified version of Hans' patch for mantis 1985. Also remove unused port_connections...
[ardour.git] / libs / ardour / midi_clock_slave.cc
index c3ca87c651897b0a3256b13186c95327b1b80360..1182fed23d9676e01557682035ec4d5d18437532 100644 (file)
 #include <poll.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <pbd/error.h>
-#include <pbd/failed_constructor.h>
-#include <pbd/pthread_utils.h>
+#include "pbd/error.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/pthread_utils.h"
 
-#include <midi++/port.h>
-#include <midi++/jack.h>
-#include <ardour/slave.h>
-#include <ardour/session.h>
-#include <ardour/audioengine.h>
-#include <ardour/cycles.h>
-#include <ardour/tempo.h>
+#include "midi++/port.h"
+#include "midi++/jack.h"
+#include "ardour/slave.h"
+#include "ardour/session.h"
+#include "ardour/audioengine.h"
+#include "ardour/cycles.h"
+#include "ardour/tempo.h"
 
 
 #include "i18n.h"
 
+using namespace std;
 using namespace ARDOUR;
 using namespace sigc;
 using namespace MIDI;
 using namespace PBD;
 
 MIDIClock_Slave::MIDIClock_Slave (Session& s, MIDI::Port& p, int ppqn)
-       : session (s)
-       , ppqn (ppqn)
+       : ppqn (ppqn)
        , bandwidth (30.0 / 60.0) // 1 BpM = 1 / 60 Hz
 {
+       session = (ISlaveSessionProxy *) new SlaveSessionProxy(s);
        rebind (p);
        reset ();
 }
 
+MIDIClock_Slave::MIDIClock_Slave (ISlaveSessionProxy* session_proxy, int ppqn)
+       : session(session_proxy)
+       , ppqn (ppqn)
+       , bandwidth (30.0 / 60.0) // 1 BpM = 1 / 60 Hz
+{
+       session = session_proxy;
+       reset ();
+}
+
 MIDIClock_Slave::~MIDIClock_Slave()
 {
+  delete session;
 }
 
 void
@@ -78,10 +89,10 @@ MIDIClock_Slave::rebind (MIDI::Port& p)
 void 
 MIDIClock_Slave::calculate_one_ppqn_in_frames_at(nframes_t time)
 {
-       const Tempo& current_tempo = session.tempo_map().tempo_at(time);
-       const Meter& current_meter = session.tempo_map().meter_at(time);
+       const Tempo& current_tempo = session->tempo_map().tempo_at(time);
+       const Meter& current_meter = session->tempo_map().meter_at(time);
        double frames_per_beat =
-               current_tempo.frames_per_beat(session.frame_rate(),
+               current_tempo.frames_per_beat(session->frame_rate(),
                                              current_meter);
 
        double quarter_notes_per_beat = 4.0 / current_tempo.note_type();
@@ -107,28 +118,27 @@ void
 MIDIClock_Slave::calculate_filter_coefficients()
 {
        // omega = 2 * PI * Bandwidth / MIDI clock frame frequency in Hz
-       omega = 2.0 * 3.14159265358979323846 * bandwidth * one_ppqn_in_frames / session.frame_rate();
+       omega = 2.0 * 3.14159265358979323846 * bandwidth * one_ppqn_in_frames / session->frame_rate();
        b = 1.4142135623730950488 * omega;
        c = omega * omega;      
 }
 
 void
-MIDIClock_Slave::update_midi_clock (Parser& parser, nframes_t timestamp)
+MIDIClock_Slave::update_midi_clock (Parser& /*parser*/, nframes_t timestamp)
 {      
        // some pieces of hardware send MIDI Clock all the time                         
        if ( (!_starting) && (!_started) ) {
                return;
        }
-       
-       // the number of midi clock messages (zero-based)
-       static long midi_clock_count;
-       
+               
        calculate_one_ppqn_in_frames_at(should_be_position);
        
        nframes_t elapsed_since_start = timestamp - first_timestamp;
        double error = 0;
        
-       if (_starting || last_timestamp == 0) {         
+       if (_starting || last_timestamp == 0) { 
+               midi_clock_count = 0;
+               
                first_timestamp = timestamp;
                elapsed_since_start = should_be_position;
                
@@ -136,8 +146,8 @@ MIDIClock_Slave::update_midi_clock (Parser& parser, nframes_t timestamp)
                calculate_filter_coefficients();
                
                // initialize DLL
-               e2 = double(one_ppqn_in_frames) / double(session.frame_rate());
-               t0 = double(elapsed_since_start) / double(session.frame_rate());
+               e2 = double(one_ppqn_in_frames) / double(session->frame_rate());
+               t0 = double(elapsed_since_start) / double(session->frame_rate());
                t1 = t0 + e2;
                
                // let ardour go after first MIDI Clock Event
@@ -148,11 +158,11 @@ MIDIClock_Slave::update_midi_clock (Parser& parser, nframes_t timestamp)
                calculate_filter_coefficients();
 
                // calculate loop error
-               // we use session.transport_frame() instead of t1 here
+               // we use session->transport_frame() instead of t1 here
                // because t1 is used to calculate the transport speed,
                // so the loop will compensate for accumulating rounding errors
-               error = (double(should_be_position) - double(session.audible_frame())); 
-               e = error / double(session.frame_rate());
+               error = (double(should_be_position) - double(session->audible_frame())); 
+               e = error / double(session->frame_rate());
                
                // update DLL
                t0 = t1;
@@ -166,29 +176,29 @@ MIDIClock_Slave::update_midi_clock (Parser& parser, nframes_t timestamp)
                                  //<< "@" << timestamp  
                                  << " arrived at: " << elapsed_since_start << " (elapsed time) " 
                                  << " should-be transport: " << should_be_position 
-                                 << " audible: " << session.audible_frame()
-                                 << " real transport: " << session.transport_frame()
+                                 << " audible: " << session->audible_frame()
+                                 << " real transport: " << session->transport_frame()
                                  << " error: " << error
-                                 //<< " engine: " << session.engine().frame_time() 
+                                 //<< " engine: " << session->frame_time() 
                                  << " real delta: " << timestamp - last_timestamp 
                                  << " should-be delta: " << one_ppqn_in_frames
-                                 << " t1-t0: " << (t1 -t0) * session.frame_rate()
-                                 << " t0: " << t0 * session.frame_rate()
-                                 << " t1: " << t1 * session.frame_rate() 
-                                 << " frame-rate: " << session.frame_rate() 
+                                 << " t1-t0: " << (t1 -t0) * session->frame_rate()
+                                 << " t0: " << t0 * session->frame_rate()
+                                 << " t1: " << t1 * session->frame_rate() 
+                                 << " frame-rate: " << session->frame_rate() 
                                  << endl;
                
-               cerr      << "frames since cycle start: " << session.engine().frames_since_cycle_start() << endl;
+               cerr      << "frames since cycle start: " << session->frames_since_cycle_start() << endl;
        #endif // DEBUG_MIDI_CLOCK
 
        last_timestamp = timestamp;
 }
 
 void
-MIDIClock_Slave::start (Parser& parser, nframes_t timestamp)
+MIDIClock_Slave::start (Parser& /*parser*/, nframes_t /*timestamp*/)
 {      
        #ifdef DEBUG_MIDI_CLOCK 
-               cerr << "MIDIClock_Slave got start message at time "  <<  timestamp << " engine time: " << session.engine().frame_time() << endl;
+               cerr << "MIDIClock_Slave got start message at time "  <<  timestamp << " engine time: " << session->frame_time() << endl;
        #endif
        
        if (!_started) {
@@ -209,11 +219,11 @@ MIDIClock_Slave::reset ()
        _starting = false;
        _started  = false;
        
-       session.request_locate(0, false);
+       session->request_locate(0, false);
 }
 
 void
-MIDIClock_Slave::contineu (Parser& parser, nframes_t timestamp)
+MIDIClock_Slave::contineu (Parser& /*parser*/, nframes_t /*timestamp*/)
 {
        #ifdef DEBUG_MIDI_CLOCK 
                std::cerr << "MIDIClock_Slave got continue message" << endl;
@@ -226,7 +236,7 @@ MIDIClock_Slave::contineu (Parser& parser, nframes_t timestamp)
 
 
 void
-MIDIClock_Slave::stop (Parser& parser, nframes_t timestamp)
+MIDIClock_Slave::stop (Parser& /*parser*/, nframes_t /*timestamp*/)
 {
        #ifdef DEBUG_MIDI_CLOCK 
                std::cerr << "MIDIClock_Slave got stop message" << endl;
@@ -236,14 +246,29 @@ MIDIClock_Slave::stop (Parser& parser, nframes_t timestamp)
                _starting = false;
                _started  = false;
                // locate to last MIDI clock position
-               session.request_transport_speed(0.0);
-               session.request_locate(should_be_position, false);
+               session->request_transport_speed(0.0);
+               
+               // we need to go back to the last MIDI beat (6 ppqn)
+               // and lets hope the tempo didnt change in the meantime :)
+               
+               // begin at the should be position, because
+               // that is the position of the last MIDI Clock
+               // message and that is probably what the master
+               // expects where we are right now
+               nframes_t stop_position = should_be_position;
+               
+               // find out the last MIDI beat: go back #midi_clocks mod 6
+               // and lets hope the tempo didnt change in those last 6 beats :)
+               stop_position -= (midi_clock_count % 6) * one_ppqn_in_frames;
+               
+               session->request_locate(stop_position, false);
+               should_be_position = stop_position;
                last_timestamp = 0;
        }
 }
 
 void
-MIDIClock_Slave::position (Parser& parser, byte* message, size_t size)
+MIDIClock_Slave::position (Parser& /*parser*/, byte* message, size_t size)
 {
        // we are note supposed to get position messages while we are running
        // so lets be robust and ignore those
@@ -263,7 +288,7 @@ MIDIClock_Slave::position (Parser& parser, byte* message, size_t size)
        cerr << "Song Position: " << position_in_sixteenth_notes << " frames: " << position_in_frames << endl; 
        #endif
        
-       session.request_locate(position_in_frames, false);
+       session->request_locate(position_in_frames, false);
        should_be_position  = position_in_frames;
        last_timestamp = 0;
        
@@ -293,13 +318,13 @@ MIDIClock_Slave::stop_if_no_more_clock_events(nframes_t& pos, nframes_t now)
        /* no timecode for 1/4 second ? conclude that its stopped */
        if (last_timestamp && 
            now > last_timestamp && 
-           now - last_timestamp > session.frame_rate() / 4) {
+           now - last_timestamp > session->frame_rate() / 4) {
         #ifdef DEBUG_MIDI_CLOCK                        
                        cerr << "No MIDI Clock frames received for some time, stopping!" << endl;
         #endif         
                pos = should_be_position;
-               session.request_transport_speed (0);
-               session.request_locate (should_be_position, false);
+               session->request_transport_speed (0);
+               session->request_locate (should_be_position, false);
                return true;
        } else {
                return false;
@@ -315,14 +340,14 @@ MIDIClock_Slave::speed_and_position (double& speed, nframes_t& pos)
                return true;
        }
                
-       nframes_t engine_now = session.engine().frame_time();
+       nframes_t engine_now = session->frame_time();
        
        if (stop_if_no_more_clock_events(pos, engine_now)) {
                return false;
        }
 
        // calculate speed
-       speed = ((t1 - t0) * session.frame_rate()) / one_ppqn_in_frames;
+       speed = ((t1 - t0) * session->frame_rate()) / one_ppqn_in_frames;
        
        // calculate position
        if (engine_now > last_timestamp) {
@@ -336,7 +361,7 @@ MIDIClock_Slave::speed_and_position (double& speed, nframes_t& pos)
        }
        
        #ifdef DEBUG_MIDI_CLOCK                 
-       cerr << "speed_and_position: " << speed << " & " << pos << " <-> " << session.transport_frame() << " (transport)" << endl;
+       cerr << "speed_and_position: " << speed << " & " << pos << " <-> " << session->transport_frame() << " (transport)" << endl;
        #endif  
        
        return true;