Fix importing to a fixed-point format with resampling
[ardour.git] / libs / ardour / transport_master.cc
index 284a0a8537b3f4837080a0b9f007929053e3da14..2564f87c5dd55e0717299696aaf12fbc4863b4e8 100644 (file)
 
 #include <vector>
 
-#include "pbd/i18n.h"
+#include "pbd/debug.h"
 
 #include "ardour/audioengine.h"
+#include "ardour/debug.h"
 #include "ardour/midi_port.h"
 #include "ardour/session.h"
 #include "ardour/transport_master.h"
 #include "ardour/transport_master_manager.h"
+#include "ardour/types_convert.h"
 #include "ardour/utils.h"
 
+#include "pbd/i18n.h"
+
+namespace ARDOUR {
+       namespace Properties {
+               PBD::PropertyDescriptor<bool> fr2997;
+               PBD::PropertyDescriptor<bool> sclock_synced;
+               PBD::PropertyDescriptor<bool> collect;
+               PBD::PropertyDescriptor<bool> connected;
+               PBD::PropertyDescriptor<TransportRequestType> allowed_transport_requests;
+       }
+}
+
 using namespace ARDOUR;
+using namespace PBD;
 
-const std::string TransportMaster::state_node_name = X_("TransportMaster");
+void
+TransportMaster::make_property_quarks ()
+{
+       Properties::fr2997.property_id = g_quark_from_static_string (X_("fr2997"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fr2997 = %1\n", Properties::fr2997.property_id));
+       Properties::sclock_synced.property_id = g_quark_from_static_string (X_("sclock_synced"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sclock_synced = %1\n", Properties::sclock_synced.property_id));
+       Properties::collect.property_id = g_quark_from_static_string (X_("collect"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for collect = %1\n", Properties::collect.property_id));
+       Properties::connected.property_id = g_quark_from_static_string (X_("connected"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for connected = %1\n", Properties::connected.property_id));
+       Properties::allowed_transport_requests.property_id = g_quark_from_static_string (X_("allowed_transport_requests"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for allowed_transport_requests = %1\n", Properties::allowed_transport_requests.property_id));
+}
 
+const std::string TransportMaster::state_node_name = X_("TransportMaster");
 
 TransportMaster::TransportMaster (SyncSource t, std::string const & name)
        : _type (t)
-       , _name (name)
+       , _name (Properties::name, name)
        , _session (0)
-       , _connected (false)
        , _current_delta (0)
-       , _collect (true)
        , _pending_collect (true)
-       , _request_mask (TransportRequestType (0))
-       , _sclock_synced (false)
+       , _removeable (false)
+       , _request_mask (Properties::allowed_transport_requests, TransportRequestType (0))
+       , _locked (Properties::locked, false)
+       , _sclock_synced (Properties::sclock_synced, false)
+       , _collect (Properties::collect, true)
+       , _connected (Properties::connected, false)
 {
+       register_properties ();
+
        ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect_same_thread (port_connection, boost::bind (&TransportMaster::connection_handler, this, _1, _2, _3, _4, _5));
        ARDOUR::AudioEngine::instance()->Running.connect_same_thread (backend_connection, boost::bind (&TransportMaster::check_backend, this));
 }
 
 TransportMaster::~TransportMaster()
 {
-       delete _session;
+}
+
+bool
+TransportMaster::speed_and_position (double& speed, samplepos_t& pos, samplepos_t& lp, samplepos_t& when, samplepos_t now)
+{
+       if (!_collect) {
+               return false;
+       }
+
+       SafeTime last;
+       current.safe_read (last);
+
+       if (last.timestamp == 0) {
+               return false;
+       }
+
+       if (last.timestamp && now > last.timestamp && now - last.timestamp > (2.0 * update_interval())) {
+               /* no timecode for two cycles - conclude that it's stopped */
+
+               if (!Config->get_transport_masters_just_roll_when_sync_lost()) {
+                       speed = 0;
+                       pos = last.position;
+                       lp = last.position;
+                       when = last.timestamp;
+                       _current_delta = 0;
+                       // queue_reset (false);
+                       DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 not seen since %2 vs %3 (%4) with seekahead = %5 reset pending, pos = %6\n", name(), last.timestamp, now, (now - last.timestamp), update_interval(), pos));
+                       return false;
+               }
+       }
+
+       lp = last.position;
+       when = last.timestamp;
+       speed = last.speed;
+       pos   = last.position + (now - last.timestamp) * last.speed;
+
+       DEBUG_TRACE (DEBUG::Slave, string_compose ("%1: speed_and_position tme: %2 pos: %3 spd: %4\n", name(), last.timestamp, last.position, last.speed));
+
+       lp = last.position;
+       when = last.timestamp;
+       speed = last.speed;
+
+       /* provide a .1% deadzone to lock the speed */
+       if (fabs (speed - 1.0) <= 0.001) {
+               speed = 1.0;
+       }
+
+       pos = last.position + (now - last.timestamp) * speed;
+
+       DEBUG_TRACE (DEBUG::Slave, string_compose ("%1 sync spd: %2 pos: %3 | last-pos: %4 | elapsed: %5\n",
+                                                  name(), speed, pos, last.position, (now - last.timestamp)));
+
+       return true;
+}
+
+void
+TransportMaster::register_properties ()
+{
+       _xml_node_name = state_node_name;
+
+       add_property (_name);
+       add_property (_locked);
+       add_property (_collect);
+       add_property (_sclock_synced);
+       add_property (_request_mask);
+
+       /* we omit _connected since it is derived from port state, and merely
+        * used for signalling
+        */
+}
+
+void
+TransportMaster::set_name (std::string const & str)
+{
+       if (_name != str) {
+               _name = str;
+               PropertyChanged (Properties::name);
+       }
 }
 
 bool
@@ -66,12 +176,19 @@ TransportMaster::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string
 
                /* it's about us */
 
+               /* XXX technically .. if the user makes an N->1 connection to
+                * this transport master's port, this simple minded logic is
+                * not sufficient. But the user shouldn't do that ...
+                */
+
                if (yn) {
                        _connected = true;
                } else {
                        _connected = false;
                }
 
+               PropertyChanged (Properties::connected);
+
                return true;
        }
 
@@ -97,8 +214,8 @@ TransportMaster::check_collect()
                                }
                        }
                }
-               std::cerr << name() << " pc = " << _pending_collect << " c = " << _collect << std::endl;
                _collect = _pending_collect;
+               PropertyChanged (Properties::collect);
        }
 
        return _collect;
@@ -107,13 +224,25 @@ TransportMaster::check_collect()
 void
 TransportMaster::set_collect (bool yn)
 {
-       _pending_collect = yn;
+       /* theoretical race condition */
+
+       if (_connected) {
+               _pending_collect = yn;
+       } else {
+               if (_collect != yn) {
+                       _pending_collect = _collect = yn;
+                       PropertyChanged (Properties::collect);
+               }
+       }
 }
 
 void
 TransportMaster::set_sample_clock_synced (bool yn)
 {
-       _sclock_synced = yn;
+       if (yn != _sclock_synced) {
+               _sclock_synced = yn;
+               PropertyChanged (Properties::sclock_synced);
+       }
 }
 
 void
@@ -125,20 +254,9 @@ TransportMaster::set_session (Session* s)
 int
 TransportMaster::set_state (XMLNode const & node, int /* version */)
 {
-       if (!node.get_property (X_("collect"), _collect)) {
-               _collect = false;
-       }
-
-       if (!node.get_property (X_("clock-synced"), _sclock_synced)) {
-               _sclock_synced = false;
-       }
+       PropertyChange what_changed;
 
-       TimecodeTransportMaster* ttm = dynamic_cast<TimecodeTransportMaster*> (this);
-       if (ttm) {
-               bool val;
-               node.get_property (X_("fr2997"), val);
-               ttm->set_fr2997 (val);
-       }
+       what_changed = set_values (node);
 
        XMLNode* pnode = node.child (X_("Port"));
 
@@ -157,6 +275,8 @@ TransportMaster::set_state (XMLNode const & node, int /* version */)
                }
        }
 
+       PropertyChanged (what_changed);
+
        return 0;
 }
 
@@ -165,14 +285,9 @@ TransportMaster::get_state ()
 {
        XMLNode* node = new XMLNode (state_node_name);
        node->set_property (X_("type"), _type);
-       node->set_property (X_("name"), _name);
-       node->set_property (X_("collect"), _collect);
-       node->set_property (X_("clock-synced"), _sclock_synced);
+       node->set_property (X_("removeable"), _removeable);
 
-       TimecodeTransportMaster* ttm = dynamic_cast<TimecodeTransportMaster*> (this);
-       if (ttm) {
-               node->set_property (X_("fr2997"), ttm->fr2997());
-       }
+       add_properties (*node);
 
        if (_port) {
                std::vector<std::string> connections;
@@ -215,6 +330,7 @@ TransportMaster::factory (XMLNode const & node)
 
        SyncSource type;
        std::string name;
+       bool removeable;
 
        if (!node.get_property (X_("type"), type)) {
                return boost::shared_ptr<TransportMaster>();
@@ -224,28 +340,95 @@ TransportMaster::factory (XMLNode const & node)
                return boost::shared_ptr<TransportMaster>();
        }
 
-       return factory (type, name);
+       if (!node.get_property (X_("removeable"), removeable)) {
+               /* development versions of 6.0 didn't have this property for a
+                  while. Any TM listed in XML at that time was non-removeable
+               */
+               removeable = false;
+       }
+
+       DEBUG_TRACE (DEBUG::Slave, string_compose ("xml-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
+
+       return factory (type, name, removeable);
 }
 
 boost::shared_ptr<TransportMaster>
-TransportMaster::factory (SyncSource type, std::string const& name)
+TransportMaster::factory (SyncSource type, std::string const& name, bool removeable)
 {
        /* XXX need to count existing sources of a given type */
 
+       boost::shared_ptr<TransportMaster> tm;
+
+       DEBUG_TRACE (DEBUG::Slave, string_compose ("factory-construct %1 name %2 removeable %3\n", enum_2_string (type), name, removeable));
+
        switch (type) {
        case MTC:
-               return boost::shared_ptr<TransportMaster> (new MTC_TransportMaster (sync_source_to_string (type)));
+               tm.reset (new MTC_TransportMaster (name));
+               break;
        case LTC:
-               return boost::shared_ptr<TransportMaster> (new LTC_TransportMaster (sync_source_to_string (type)));
+               tm.reset (new LTC_TransportMaster (name));
+               break;
        case MIDIClock:
-               return boost::shared_ptr<TransportMaster> (new MIDIClock_TransportMaster (sync_source_to_string (type)));
+               tm.reset (new MIDIClock_TransportMaster (name));
+               break;
        case Engine:
-               return boost::shared_ptr<TransportMaster> (new Engine_TransportMaster (*AudioEngine::instance()));
+               tm.reset (new Engine_TransportMaster (*AudioEngine::instance()));
+               break;
        default:
                break;
        }
 
-       return boost::shared_ptr<TransportMaster>();
+       if (tm) {
+               tm->set_removeable (removeable);
+       }
+
+       return tm;
+}
+
+/** @param sh Return a short version of the string */
+std::string
+TransportMaster::display_name (bool sh) const
+{
+
+       switch (_type) {
+       case Engine:
+               /* no other backends offer sync for now ... deal with this if we
+                * ever have to.
+                */
+               return S_("SyncSource|JACK");
+
+       case MTC:
+               if (sh) {
+                       if (name().length() <= 4) {
+                               return name();
+                       }
+                       return S_("SyncSource|MTC");
+               } else {
+                       return name();
+               }
+
+       case MIDIClock:
+               if (sh) {
+                       if (name().length() <= 4) {
+                               return name();
+                       }
+                       return S_("SyncSource|M-Clk");
+               } else {
+                       return name();
+               }
+
+       case LTC:
+               if (sh) {
+                       if (name().length() <= 4) {
+                               return name();
+                       }
+                       return S_("SyncSource|LTC");
+               } else {
+                       return name();
+               }
+       }
+       /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
+       return S_("SyncSource|JACK");
 }
 
 boost::shared_ptr<Port>
@@ -271,11 +454,32 @@ TransportMaster::allow_request (TransportRequestSource src, TransportRequestType
 void
 TransportMaster::set_request_mask (TransportRequestType t)
 {
-       _request_mask = t;
+       if (_request_mask != t) {
+               _request_mask = t;
+               PropertyChanged (Properties::allowed_transport_requests);
+       }
+}
+
+TimecodeTransportMaster::TimecodeTransportMaster (std::string const & name, SyncSource type)
+       : TransportMaster (type, name)
+       , _fr2997 (Properties::fr2997, false)
+{
+       register_properties ();
+}
+
+void
+TimecodeTransportMaster::register_properties ()
+{
+       TransportMaster::register_properties ();
+       add_property (_fr2997);
 }
 
 void
 TimecodeTransportMaster::set_fr2997 (bool yn)
 {
-       _fr2997 = yn;
+       if (yn != _fr2997) {
+               _fr2997 = yn;
+               PropertyChanged (Properties::fr2997);
+       }
 }
+