* made MidiClock_Slave conform more to to the Spec by starting transport
[ardour.git] / libs / ardour / midi_clock_slave.cc
index 42ce24c4389e4aa5b1eadc5c1f9c277abcda1d7b..e6639446d58a78d82a7a992e4a7fe9a43c876bf7 100644 (file)
 #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 "i18n.h"
 
 using namespace ARDOUR;
@@ -43,7 +45,7 @@ MIDIClock_Slave::MIDIClock_Slave (Session& s, MIDI::Port& p, int ppqn)
        : session (s)
        , ppqn (ppqn)
        , accumulator_index (0)
-       , average (0.0)
+       , average_midi_clock_frame_duration (0.0)
 {
        rebind (p);
        reset ();
@@ -65,61 +67,87 @@ MIDIClock_Slave::rebind (MIDI::Port& p)
 
        port = &p;
 
+#ifdef DEBUG_MIDI_CLOCK                
        std::cerr << "MIDIClock_Slave: connecting to port " << port->name() << std::endl;
+#endif
 
-       connections.push_back (port->input()->timing.connect (mem_fun (*this, &MIDIClock_Slave::update_midi_clock)));
-       connections.push_back (port->input()->start.connect  (mem_fun (*this, &MIDIClock_Slave::start)));
-       connections.push_back (port->input()->stop.connect   (mem_fun (*this, &MIDIClock_Slave::stop)));
+       connections.push_back (port->input()->timing.connect   (mem_fun (*this, &MIDIClock_Slave::update_midi_clock)));
+       connections.push_back (port->input()->start.connect    (mem_fun (*this, &MIDIClock_Slave::start)));
+       connections.push_back (port->input()->contineu.connect (mem_fun (*this, &MIDIClock_Slave::contineu)));
+       connections.push_back (port->input()->stop.connect     (mem_fun (*this, &MIDIClock_Slave::stop)));
 }
 
-void
-MIDIClock_Slave::update_midi_clock (Parser& parser)
-{      
-       nframes_t now = session.engine().frame_time();
-
-       SafeTime last;
-       read_current (&last);
-               
-       const Tempo& current_tempo = session.tempo_map().tempo_at(now);
-       const Meter& current_meter = session.tempo_map().meter_at(now);
+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);
        double frames_per_beat =
-               current_tempo.frames_per_beat(session.frame_rate(),
+               current_tempo.frames_per_beat(session.nominal_frame_rate(),
                                              current_meter);
 
        double quarter_notes_per_beat = 4.0 / current_tempo.note_type();
        double frames_per_quarter_note = frames_per_beat / quarter_notes_per_beat;
 
-       one_ppqn_in_frames = frames_per_quarter_note / ppqn;
+       one_ppqn_in_frames = frames_per_quarter_note / double (ppqn);
+}
+
+void
+MIDIClock_Slave::update_midi_clock (Parser& parser, nframes_t timestamp)
+{      
+       nframes_t now = timestamp;
+
+       SafeTime last;
+       read_current (&last);
+       
+       if (_starting) {
+               assert(last.timestamp == 0);
+               // let ardour go after first MIDI Clock Event
+               _starting = false;
+       }
+               
+       calculate_one_ppqn_in_frames_at(now);
        
-       // for the first MIDI clock event we dont have any past
+       // for the first MIDI clock event we don't have any past
        // data, so we assume a sane tempo
        if(last.timestamp == 0) {
-               midi_clock_frame = one_ppqn_in_frames;
+               current_midi_clock_frame_duration = one_ppqn_in_frames;
        } else {
-               midi_clock_frame = now - last.timestamp;
+               current_midi_clock_frame_duration = now - last.timestamp;
        }
-       
+               
        // moving average over incoming intervals
-       accumulator[accumulator_index++] = (float) midi_clock_frame;
+       accumulator[accumulator_index++] = current_midi_clock_frame_duration;
        if(accumulator_index == accumulator_size)
                accumulator_index = 0;
        
-       average = 0.0;
+       average_midi_clock_frame_duration = 0.0;
        for(int i = 0; i < accumulator_size; i++)
-               average += accumulator[i];
-       average /= accumulator_size;
+               average_midi_clock_frame_duration += accumulator[i];
+       average_midi_clock_frame_duration /= double(accumulator_size);
        
+#ifdef DEBUG_MIDI_CLOCK                
+#ifdef WITH_JACK_MIDI
+       JACK_MidiPort* jack_port = dynamic_cast<JACK_MidiPort*>(port);
+       pthread_t process_thread_id = 0;
+       if(jack_port) {
+               process_thread_id = jack_port->get_process_thread();
+       }
        
-       std::cerr << "got MIDI Clock message at time " << now  
-                 << " real delta: " << midi_clock_frame 
+       std::cerr 
+                 << " got MIDI Clock message at time " << now  
+                 << " session time: " << session.engine().frame_time() 
+                 << " transport position: " << session.transport_frame()
+                 << " real delta: " << current_midi_clock_frame_duration 
                  << " reference: " << one_ppqn_in_frames
-                 << " accu index: " << accumulator_index
-                 << " average: " << average
-                 << " locked: " << locked()
-                 << " frame rate: " << session.frame_rate() << std::endl;
+                 << " average: " << average_midi_clock_frame_duration
+                 << std::endl;
+#endif // DEBUG_MIDI_CLOCK
+#endif // WITH_JACK_MIDI
        
        current.guard1++;
-       current.position += midi_clock_frame;
+       current.position += one_ppqn_in_frames;
+       current_position += one_ppqn_in_frames;
        current.timestamp = now;
        current.guard2++;
 
@@ -127,41 +155,57 @@ MIDIClock_Slave::update_midi_clock (Parser& parser)
 }
 
 void
-MIDIClock_Slave::start (Parser& parser)
+MIDIClock_Slave::start (Parser& parser, nframes_t timestamp)
 {
        
-       nframes_t now = session.engine().frame_time();
-       cerr << "MIDIClock_Slave got start message at time " 
-                 <<  now << endl;
-
+       nframes_t now = timestamp;
+       
+#ifdef DEBUG_MIDI_CLOCK        
+       cerr << "MIDIClock_Slave got start message at time "  <<  now << " session time: " << session.engine().frame_time() << endl;
+#endif
+       
        if(!locked()) {
                cerr << "Did not start because not locked!" << endl;
                return;
        }
        
-       midi_clock_frame = 0;
-
-       session.request_transport_speed (1.0);
-       midi_clock_speed = 1.0;
-       first_midi_clock_time = now;
+       current_midi_clock_frame_duration = 0;
        
        current.guard1++;
        current.position = 0;
-       current.timestamp = now;
+       current_position = 0;   
+       current.timestamp = 0;
        current.guard2++;
        
        _started = true;
+       _starting = true;
 }
 
 void
-MIDIClock_Slave::stop (Parser& parser)
+MIDIClock_Slave::contineu (Parser& parser, nframes_t timestamp)
 {
-       std::cerr << "MIDIClock_Slave got stop message" << endl;
+#ifdef DEBUG_MIDI_CLOCK        
+       std::cerr << "MIDIClock_Slave got continue message" << endl;
+#endif
+       start(parser, timestamp);
+}
 
-       session.request_transport_speed (0);
-       midi_clock_speed = 0.0f;
-       midi_clock_frame = 0;
 
+void
+MIDIClock_Slave::stop (Parser& parser, nframes_t timestamp)
+{
+#ifdef DEBUG_MIDI_CLOCK        
+       std::cerr << "MIDIClock_Slave got stop message" << endl;
+#endif
+       
+       current_midi_clock_frame_duration = 0;
+
+       current.guard1++;
+       current.position = 0;
+       current_position = 0;   
+       current.timestamp = 0;
+       current.guard2++;
+       
        _started = false;
        reset();
 }
@@ -186,14 +230,7 @@ MIDIClock_Slave::read_current (SafeTime *st) const
 bool
 MIDIClock_Slave::locked () const
 {
-       float rel_error = 
-               ( fabs(one_ppqn_in_frames - average) / one_ppqn_in_frames ); 
-       //cerr << " relative error: " << rel_error << endl;
-       
-       return rel_error < 0.01 ?
-                       true
-               :
-                       false;
+       return true;
 }
 
 bool
@@ -202,50 +239,81 @@ MIDIClock_Slave::ok() const
        return true;
 }
 
+bool
+MIDIClock_Slave::starting() const
+{
+       return _starting;
+}
+
+bool
+MIDIClock_Slave::stop_if_no_more_clock_events(nframes_t& pos, nframes_t now, SafeTime& last)
+{
+       /* no timecode for 1/4 second ? conclude that its stopped */
+       if (last_inbound_frame && 
+           now > last_inbound_frame && 
+           now - last_inbound_frame > session.frame_rate() / 4) {
+#ifdef DEBUG_MIDI_CLOCK                        
+               cerr << "No MIDI Clock frames received for some time, stopping!" << endl;
+#endif         
+               pos = last.position;
+               session.request_locate (pos, false);
+               session.request_transport_speed (0);
+               this->stop(*port->input(), now);
+               reset();
+               return true;
+       } else {
+               return false;
+       }
+}
+
 bool
 MIDIClock_Slave::speed_and_position (float& speed, nframes_t& pos)
 {
-       
-       if(!_started) {
+       if (!_started) {
                speed = 0.0;
                pos = 0;
                return true;
        }
-       
+               
        nframes_t now = session.engine().frame_time();
-       nframes_t frame_rate = session.frame_rate();
-
        SafeTime last;
        read_current (&last);
 
-       /* no timecode for 1/4 second ? conclude that its stopped */
-
-       if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > frame_rate / 4) {
-               midi_clock_speed = 0;
-               pos = last.position;
-               session.request_locate (pos, false);
-               session.request_transport_speed (0);
-               this->stop(*port->input());
-               reset();
+       if (stop_if_no_more_clock_events(pos, now, last)) {
                return false;
        }
+       //cerr << " now: " << now << " last: " << last.timestamp;
 
-       midi_clock_speed = average / one_ppqn_in_frames;
-
-       nframes_t elapsed = now - last.timestamp;
+       // calculate speed
+       double speed_double = one_ppqn_in_frames / average_midi_clock_frame_duration;
+       speed = float(speed_double);
+       //cerr << " final speed: " << speed;
        
-       pos = last.position + elapsed * speed;
-
-       speed = midi_clock_speed;
+       // calculate position
+       if (now > last.timestamp) {
+               // we are in between MIDI clock messages
+               // so we interpolate position according to speed
+               nframes_t elapsed = now - last.timestamp;
+               pos = nframes_t (current_position + double(elapsed) * speed_double);
+       } else {
+               // A new MIDI clock message has arrived this cycle
+               pos = current_position;
+       }
        
-       cerr << " final speed: " << speed << " elapsed: " << elapsed << " elapsed (scaled)  " << elapsed * speed << " position: " << pos << endl;
+       /*
+   cerr << " transport position now: " <<  session.transport_frame(); 
+   cerr << " calculated position: " << pos; 
+   cerr << endl;
+   */
+   
        return true;
 }
 
 ARDOUR::nframes_t
 MIDIClock_Slave::resolution() const
 {
-       return (nframes_t) one_ppqn_in_frames;
+       // one beat
+       return (nframes_t) one_ppqn_in_frames * ppqn;
 }
 
 void
@@ -255,10 +323,9 @@ MIDIClock_Slave::reset ()
        last_inbound_frame = 0;
        current.guard1++;
        current.position = 0;
+       current_position = 0;           
        current.timestamp = 0;
        current.guard2++;
-       first_midi_clock_frame = 0;
-       first_midi_clock_time = 0;
-
-       midi_clock_speed = 0;
+       
+       session.request_locate(0, false);
 }