implement TC offset for slave&generator.
authorRobin Gareus <robin@gareus.org>
Tue, 13 Nov 2012 20:29:28 +0000 (20:29 +0000)
committerRobin Gareus <robin@gareus.org>
Tue, 13 Nov 2012 20:29:28 +0000 (20:29 +0000)
Many related changes that require atomic update, mostly
because ClockOption slots changed:

  * change offset config format to std:string (backwards compat - reads 0)
  * make Timecode offset independent from fps
  * sample_to_timecode() handle negative sample-num
  * audio-clock fix entry and edit of negative numbers
  * option editor:
    - remove old global internal offset
    - add slave & generator TC entry
    - still needs UI cleanup, tooltops, maybe sep. tab..
  * LTC & MTC slave& generator:
    - cache offset
    - subscribe to parameter changes

git-svn-id: svn://localhost/ardour2/branches/3.0@13485 d708f5d6-7413-0410-9779-e7cbd77b26cf

13 files changed:
gtk2_ardour/audio_clock.cc
gtk2_ardour/option_editor.cc
gtk2_ardour/option_editor.h
gtk2_ardour/session_option_editor.cc
libs/ardour/ardour/session.h
libs/ardour/ardour/session_configuration_vars.h
libs/ardour/ardour/slave.h
libs/ardour/ltc_slave.cc
libs/ardour/mtc_slave.cc
libs/ardour/session_ltc.cc
libs/ardour/session_state.cc
libs/ardour/session_time.cc
libs/timecode/src/time.cc

index 4fa30186a7bbb9985415fab3b65d1633bfbd8737..9a9fc6d56e71911e42813b8f5c561c6dc1b51093 100644 (file)
@@ -915,7 +915,7 @@ AudioClock::end_edit_relative (bool add)
                } else {
                        framepos_t c = current_time();
 
-                       if (c > frames) {
+                       if (c > frames || _negative_allowed) {
                                set (c - frames, true);
                        } else {
                                set (0, true);
@@ -932,6 +932,11 @@ AudioClock::end_edit_relative (bool add)
 void
 AudioClock::session_configuration_changed (std::string p)
 {
+       if (_negative_allowed) {
+               /* session option editor clock */
+               return;
+       }
+
        if (p == "sync-source" || p == "external-sync") {
                set (current_time(), true);
                return;
@@ -1897,10 +1902,23 @@ bool
 AudioClock::timecode_validate_edit (const string&)
 {
        Timecode::Time TC;
+       int hours;
        char ignored[2];
 
        if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 "%[:;]%" PRId32,
-                   &TC.hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
+                   &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
+               return false;
+       }
+
+       if (hours < 0) {
+               TC.hours = hours * -1;
+               TC.negative = true;
+       } else {
+               TC.hours = hours;
+               TC.negative = false;
+       }
+
+       if (TC.negative && !_negative_allowed) {
                return false;
        }
 
@@ -1947,19 +1965,22 @@ AudioClock::frames_from_timecode_string (const string& str) const
        Timecode::Time TC;
        framepos_t sample;
        char ignored[2];
+       int hours;
 
-       if (sscanf (str.c_str(), "%d:%d:%d%[:;]%d", &TC.hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
+       if (sscanf (str.c_str(), "%d:%d:%d%[:;]%d", &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
                error << string_compose (_("programming error: %1 %2"), "badly formatted timecode clock string", str) << endmsg;
                return 0;
        }
-
-       TC.negative = edit_is_negative;
+       TC.hours = abs(hours);
        TC.rate = _session->timecode_frames_per_second();
        TC.drop= _session->timecode_drop_frames();
 
        _session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ );
 
        // timecode_tester ();
+       if (edit_is_negative) {
+               sample = - sample;
+       }
 
        return sample;
 }
index 3c3f5baab64e48a3ebc1267ba394e6fabec8c6b6..da4594e6da953fb9787387cc06205c1b765c8f2d 100644 (file)
@@ -25,6 +25,7 @@
 #include "ardour/rc_configuration.h"
 #include "ardour/utils.h"
 #include "ardour/dB.h"
+#include "ardour/session.h"
 
 #include "option_editor.h"
 #include "gui_thread.h"
@@ -284,7 +285,7 @@ FaderOption::add_to_page (OptionEditorPage* p)
        add_widgets_to_page (p, &_label, &_box);
 }
 
-ClockOption::ClockOption (string const & i, string const & n, sigc::slot<framecnt_t> g, sigc::slot<bool, framecnt_t> s)
+ClockOption::ClockOption (string const & i, string const & n, sigc::slot<std::string> g, sigc::slot<bool, std::string> s)
        : Option (i, n)
        , _clock (X_("timecode-offset"), false, X_(""), true, false, true, false)
        , _get (g)
@@ -299,13 +300,24 @@ ClockOption::ClockOption (string const & i, string const & n, sigc::slot<framecn
 void
 ClockOption::set_state_from_config ()
 {
-       _clock.set (_get (), true);
+       Timecode::Time TC;
+       framepos_t when;
+       if (!Timecode::parse_timecode_format(_get(), TC)) {
+               _clock.set (0, true);
+       }
+       TC.rate = _session->frames_per_timecode_frame();
+       TC.drop = _session->timecode_drop_frames();
+       _session->timecode_to_sample(TC, when, false, false);
+       if (TC.negative) { when=-when; }
+       _clock.set (when, true);
 }
 
 void
 ClockOption::save_clock_time ()
 {
-       _set (_clock.current_time());
+       Timecode::Time TC;
+       _session->sample_to_timecode(_clock.current_time(), TC, false, false);
+       _set (Timecode::timecode_format_time(TC));
 }
 
 void
@@ -317,6 +329,7 @@ ClockOption::add_to_page (OptionEditorPage* p)
 void
 ClockOption::set_session (Session* s)
 {
+       _session = s;
        _clock.set_session (s);
 }
 
index 454b0f22453de11cfefe2722af40ed9b0e3cbaed..5fbed4ea32d89b5514f49596993a9c56716a11e1 100644 (file)
@@ -535,7 +535,7 @@ private:
 class ClockOption : public Option
 {
 public:
-       ClockOption (std::string const &, std::string const &, sigc::slot<ARDOUR::framecnt_t>, sigc::slot<bool, ARDOUR::framecnt_t>);
+       ClockOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
        void set_state_from_config ();
        void add_to_page (OptionEditorPage *);
        void set_session (ARDOUR::Session *);
@@ -547,8 +547,9 @@ private:
        void save_clock_time ();
        Gtk::Label _label;
        AudioClock _clock;
-       sigc::slot<ARDOUR::framecnt_t> _get;
-       sigc::slot<bool, ARDOUR::framecnt_t> _set;
+       sigc::slot<std::string> _get;
+       sigc::slot<bool, std::string> _set;
+       ARDOUR::Session *_session;
 };
 
 class DirectoryOption : public Option
index dd95b7f057bbc65a242f8599065839c809d9150c..64ccc93de69d7b942659a185a94042e5fa963338 100644 (file)
@@ -91,24 +91,32 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
 
        add_option (_("Timecode"), _vpu);
 
-       ClockOption* co = new ClockOption (
-               "timecode-offset",
-               _("Timecode offset"),
-               sigc::mem_fun (*_session_config, &SessionConfiguration::get_timecode_offset),
-               sigc::mem_fun (*_session_config, &SessionConfiguration::set_timecode_offset)
+
+       add_option (_("Timecode"), new OptionEditorHeading (_("Ext Timecode Offsets")));
+
+       ClockOption* sco = new ClockOption (
+               "slave-timecode-offset",
+               _("Slave Timecode offset"),
+               sigc::mem_fun (*_session_config, &SessionConfiguration::get_slave_timecode_offset),
+               sigc::mem_fun (*_session_config, &SessionConfiguration::set_slave_timecode_offset)
                );
 
-       co->set_session (_session);
-       co->clock().set_negative_allowed (true);
+       sco->set_session (_session);
+       sco->clock().set_negative_allowed (true);
 
-       add_option (_("Timecode"), co);
+       add_option (_("Timecode"), sco);
 
-       add_option (_("Timecode"), new BoolOption (
-                           "timecode-offset-negative",
-                           _("Timecode Offset Negative"),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::get_timecode_offset_negative),
-                           sigc::mem_fun (*_session_config, &SessionConfiguration::set_timecode_offset_negative)
-                           ));
+       ClockOption* gco = new ClockOption (
+               "timecode-generator-offset",
+               _("Timecode Generator offset"),
+               sigc::mem_fun (*_session_config, &SessionConfiguration::get_timecode_generator_offset),
+               sigc::mem_fun (*_session_config, &SessionConfiguration::set_timecode_generator_offset)
+               );
+
+       gco->set_session (_session);
+       gco->clock().set_negative_allowed (true);
+
+       add_option (_("Timecode"), gco);
 
        add_option (_("Timecode"), new OptionEditorHeading (_("JACK Transport/Time Settings")));
 
@@ -300,7 +308,11 @@ SessionOptionEditor::parameter_changed (std::string const & p)
                        _vpu->set_sensitive(true);
                }
        }
-
+       if (p == "timecode-format") {
+               /* update offset clocks */
+               parameter_changed("timecode-generator-offset");
+               parameter_changed("slave-timecode-offset");
+       }
 }
 
 /* the presence of absence of a monitor section is not really a regular session
index d86b919111159a671470c14e10bd083feb41d1b5..9b25f29c8f3b2d504f04bc78ae9516d6d5c7f125 100644 (file)
@@ -1203,6 +1203,9 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
        framepos_t        ltc_enc_off;
        bool              restarting;
 
+       framepos_t        ltc_timecode_offset;
+       bool              ltc_timecode_negative_offset;
+
        jack_latency_range_t ltc_out_latency;
 
        void ltc_tx_initialize();
@@ -1210,6 +1213,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
        void ltc_tx_reset();
        void ltc_tx_resync_latency();
        void ltc_tx_recalculate_position();
+       void ltc_tx_parse_offset();
        void ltc_tx_send_time_code_for_cycle (framepos_t, framepos_t, double, double, pframes_t nframes);
 #endif
 
index 1bfc25492149f1c633741f8ff3d2fb55c1f1d99a..1a4a17ce5d9b3081a4882bf9ff6e878f03f6d3e1 100644 (file)
@@ -53,10 +53,8 @@ CONFIG_VARIABLE (bool, external_sync, "external-sync", false)
 CONFIG_VARIABLE (InsertMergePolicy, insert_merge_policy, "insert-merge-policy", InsertMergeRelax)
 CONFIG_VARIABLE (framecnt_t, timecode_offset, "timecode-offset", 0)
 CONFIG_VARIABLE (bool, timecode_offset_negative, "timecode-offset-negative", true)
-CONFIG_VARIABLE (framecnt_t, slave_timecode_offset, "slave-timecode-offset", 0)
-CONFIG_VARIABLE (bool, slave_timecode_offset_negative, "slave-timecode-offset-negative", true)
-CONFIG_VARIABLE (framecnt_t, timecode_generator_offset, "timecode-generator-offset", 0)
-CONFIG_VARIABLE (bool, timecode_generator_offset_negative, "timecode-generator-offset-negative", true)
+CONFIG_VARIABLE (std::string, slave_timecode_offset, "slave-timecode-offset", " 00:00:00:00")
+CONFIG_VARIABLE (std::string, timecode_generator_offset, "timecode-generator-offset", " 00:00:00:00")
 CONFIG_VARIABLE (bool, glue_new_markers_to_bars_and_beats, "glue-new-markers-to-bars-and-beats", false)
 CONFIG_VARIABLE (bool, midi_copy_is_fork, "midi-copy-is-fork", false)
 CONFIG_VARIABLE (bool, glue_new_regions_to_bars_and_beats, "glue-new-regions-to-bars-and-beats", false)
index 5b424bab6ff4762cf08f54d36e40cc6a96fe43a1..d5cb79c522b449695c3097da5b071152155d5473 100644 (file)
@@ -247,6 +247,9 @@ class TimecodeSlave : public Slave {
        of the TC source position.
     */
     virtual std::string approximate_current_position() const = 0;
+
+    framepos_t        timecode_offset;
+    bool              timecode_negative_offset;
 };
 
 class MTC_Slave : public TimecodeSlave {
@@ -274,6 +277,7 @@ class MTC_Slave : public TimecodeSlave {
        Session&    session;
        MIDI::Port* port;
        PBD::ScopedConnectionList port_connections;
+       PBD::ScopedConnection     config_connection;
        bool        can_notify_on_unknown_rate;
 
        static const int frame_tolerance;
@@ -327,6 +331,8 @@ class MTC_Slave : public TimecodeSlave {
        bool outside_window (framepos_t) const;
        void init_mtc_dll(framepos_t, double);
        void init_engine_dll (framepos_t, framepos_t);
+       void parse_timecode_offset();
+       void parameter_changed(std::string const & p);
 };
 
 #ifdef HAVE_LTC
@@ -359,6 +365,8 @@ public:
        void reset();
        void resync_xrun();
        void resync_latency();
+       void parse_timecode_offset();
+       void parameter_changed(std::string const & p);
 
        Session&       session;
        bool           did_reset_tc_format;
@@ -384,6 +392,7 @@ public:
        Timecode::TimecodeFormat a3e_timecode;
 
        PBD::ScopedConnectionList port_connections;
+       PBD::ScopedConnection     config_connection;
        jack_latency_range_t      ltc_slave_latency;
 
        /* DLL - chase LTC */
index 166c78ce2459d30c756b531039f190cdfb56aa7f..7b182cfa64949a8bf2e5176327fc5d013f4f035e 100644 (file)
@@ -60,6 +60,9 @@ LTC_Slave::LTC_Slave (Session& s)
        memset(&prev_frame, 0, sizeof(LTCFrameExt));
 
        decoder = ltc_decoder_create((int) frames_per_ltc_frame, 128 /*queue size*/);
+
+       session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&LTC_Slave::parameter_changed, this, _1));
+       parse_timecode_offset();
        reset();
        resync_latency();
        session.Xrun.connect_same_thread (port_connections, boost::bind (&LTC_Slave::resync_xrun, this));
@@ -69,6 +72,7 @@ LTC_Slave::LTC_Slave (Session& s)
 LTC_Slave::~LTC_Slave()
 {
        port_connections.drop_connections();
+       config_connection.disconnect();
 
        if (did_reset_tc_format) {
                session.config.set_timecode_format (saved_tc_format);
@@ -77,6 +81,27 @@ LTC_Slave::~LTC_Slave()
        ltc_decoder_free(decoder);
 }
 
+void
+LTC_Slave::parse_timecode_offset() {
+       Timecode::Time offset_tc;
+       Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc);
+       offset_tc.rate = session.timecode_frames_per_second();
+       offset_tc.drop = session.timecode_drop_frames();
+       session.timecode_to_sample(offset_tc, timecode_offset, false, false);
+       timecode_negative_offset = offset_tc.negative;
+}
+
+void
+LTC_Slave::parameter_changed (std::string const & p)
+{
+       if (p == "slave-timecode-offset"
+                       || p == "subframes-per-frame"
+                       || p == "timecode-format"
+                       ) {
+               parse_timecode_offset();
+       }
+}
+
 ARDOUR::framecnt_t
 LTC_Slave::resolution () const
 {
@@ -334,7 +359,7 @@ LTC_Slave::process_ltc(framepos_t const now)
                Timecode::timecode_to_sample (timecode, ltc_frame, true, false,
                        double(session.frame_rate()),
                        session.config.get_subframes_per_frame(),
-                       session.config.get_slave_timecode_offset_negative(), session.config.get_slave_timecode_offset()
+                       timecode_negative_offset, timecode_offset
                        );
 
                framepos_t cur_timestamp = frame.off_end + 1;
index 1228465f8b4534a117d8cbe37adefa3f8e665ff4..dcc51ee5c85974eccc815a61288166fee4365247 100644 (file)
@@ -66,6 +66,8 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
        a3e_timecode = session.config.get_timecode_format();
        printed_timecode_warning = false;
 
+       session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
+       parse_timecode_offset();
        reset (true);
        rebind (p);
 }
@@ -73,6 +75,7 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
 MTC_Slave::~MTC_Slave()
 {
        port_connections.drop_connections();
+       config_connection.disconnect();
 
        while (busy_guard1 != busy_guard2) {
                /* make sure MIDI parser is not currently calling any callbacks in here,
@@ -101,6 +104,27 @@ MTC_Slave::rebind (MIDI::Port& p)
        port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
 }
 
+void
+MTC_Slave::parse_timecode_offset() {
+       Timecode::Time offset_tc;
+       Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc);
+       offset_tc.rate = session.timecode_frames_per_second();
+       offset_tc.drop = session.timecode_drop_frames();
+       session.timecode_to_sample(offset_tc, timecode_offset, false, false);
+       timecode_negative_offset = offset_tc.negative;
+}
+
+void
+MTC_Slave::parameter_changed (std::string const & p)
+{
+       if (p == "slave-timecode-offset"
+                       || p == "subframes-per-frame"
+                       || p == "timecode-format"
+                       ) {
+               parse_timecode_offset();
+       }
+}
+
 bool
 MTC_Slave::give_slave_full_control_over_transport_speed() const
 {
@@ -390,7 +414,7 @@ MTC_Slave::update_mtc_time (const byte *msg, bool was_full, framepos_t now)
        Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
                double(session.frame_rate()),
                session.config.get_subframes_per_frame(),
-               session.config.get_slave_timecode_offset_negative(), session.config.get_slave_timecode_offset()
+               timecode_negative_offset, timecode_offset
                );
 
        DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
index 25ba783fc7a08f0c8b8e50fc52d5501bbfc44ecf..733ca6877e64cbdf735919eaa960f933f015ff62 100644 (file)
@@ -63,6 +63,7 @@ Session::ltc_tx_initialize()
 {
        ltc_enc_tcformat = config.get_timecode_format();
 
+       ltc_tx_parse_offset();
        DEBUG_TRACE (DEBUG::LTC, string_compose("LTC TX init sr: %1 fps: %2\n", nominal_frame_rate(), timecode_to_frames_per_second(ltc_enc_tcformat)));
        ltc_encoder = ltc_encoder_create(nominal_frame_rate(),
                        timecode_to_frames_per_second(ltc_enc_tcformat),
@@ -118,6 +119,16 @@ Session::ltc_tx_reset()
        ltc_encoder_reset(ltc_encoder);
 }
 
+void
+Session::ltc_tx_parse_offset() {
+       Timecode::Time offset_tc;
+       Timecode::parse_timecode_format(config.get_timecode_generator_offset(), offset_tc);
+       offset_tc.rate = timecode_frames_per_second();
+       offset_tc.drop = timecode_drop_frames();
+       timecode_to_sample(offset_tc, ltc_timecode_offset, false, false);
+       ltc_timecode_negative_offset = !offset_tc.negative;
+}
+
 void
 Session::ltc_tx_recalculate_position()
 {
@@ -135,7 +146,7 @@ Session::ltc_tx_recalculate_position()
        Timecode::timecode_to_sample (a3tc, ltc_enc_pos, true, false,
                (double)frame_rate(),
                config.get_subframes_per_frame(),
-               config.get_timecode_generator_offset_negative(), config.get_timecode_generator_offset()
+               ltc_timecode_negative_offset, ltc_timecode_offset
                );
        restarting = false;
 }
@@ -204,6 +215,7 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end
                }
                ltc_encoder_set_filter(ltc_encoder, LTC_RISE_TIME(ltc_speed));
                ltc_enc_tcformat = cur_timecode;
+               ltc_tx_parse_offset();
                ltc_tx_reset();
        }
 
@@ -365,14 +377,14 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end
                        timecode_drop_frames(),
                        (double)frame_rate(),
                        config.get_subframes_per_frame(),
-                       config.get_timecode_generator_offset_negative(), config.get_timecode_generator_offset()
+                       ltc_timecode_negative_offset, ltc_timecode_offset
                        );
 
        /* convert timecode back to sample-position */
        Timecode::timecode_to_sample (tc_start, tc_sample_start, true, false,
                (double)frame_rate(),
                config.get_subframes_per_frame(),
-               config.get_timecode_generator_offset_negative(), config.get_timecode_generator_offset()
+               ltc_timecode_negative_offset, ltc_timecode_offset
                );
 
        /* difference between current frame and TC frame in samples */
index 46dee236ae1f986c2ac9bd74b36d60fd98f4ebb2..74257ef4826d919123e931cc6c28f97416031e0b 100644 (file)
@@ -3565,6 +3565,10 @@ Session::config_changed (std::string p, bool ours)
                reconnect_ltc_input ();
        } else if (p == "ltc-sink-port") {
                reconnect_ltc_output ();
+#ifdef HAVE_LTC
+       } else if (p == "timecode-generator-offset") {
+               ltc_tx_parse_offset();
+#endif
        }
 
        set_dirty ();
index 5e25aaf5080d28af5d428b8052e0872795980a2a..72d8fb1e89289b3d1de11f32eb7aaf4ca21b1da1 100644 (file)
@@ -95,6 +95,9 @@ Session::sync_time_vars ()
                }
                break;
        };
+#ifdef HAVE_LTC
+       ltc_tx_parse_offset();
+#endif
 }
 
 void
index b39da34d6ddeded63ba5adbed3bd955be7da89b4..9a67e177b776078052248937497eb86e242153a0 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <math.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #include "timecode/time.h"
 
@@ -761,8 +762,8 @@ sample_to_timecode (
        int64_t offset_sample;
 
        if (!use_offset) {
-               offset_sample = sample;
-               timecode.negative = false;
+               timecode.negative = (sample < 0);
+               offset_sample = llabs(sample);
        } else {
                if (offset_is_negative) {
                        offset_sample = sample + offset_samples;