new transport slave/master implementation, gui edition
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 18 Sep 2018 22:52:20 +0000 (18:52 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 18 Sep 2018 23:06:04 +0000 (19:06 -0400)
16 files changed:
gtk2_ardour/ardour.menus.in
gtk2_ardour/ardour_ui.cc
gtk2_ardour/ardour_ui.h
gtk2_ardour/ardour_ui_dialogs.cc
gtk2_ardour/ardour_ui_options.cc
gtk2_ardour/audio_clock.cc
gtk2_ardour/midi_tracer.cc
gtk2_ardour/midi_tracer.h
gtk2_ardour/option_editor.cc
gtk2_ardour/option_editor.h
gtk2_ardour/port_group.cc
gtk2_ardour/rc_option_editor.cc
gtk2_ardour/rc_option_editor.h
gtk2_ardour/transport_masters_dialog.cc [new file with mode: 0644]
gtk2_ardour/transport_masters_dialog.h [new file with mode: 0644]
gtk2_ardour/wscript

index f952a410a71dbfd71e952e9d8751a90d5ecad5cb..858af019438639a19bb5e8728d64e8848e884dd9 100644 (file)
 #endif
       <menuitem action='toggle-big-clock'/>
       <menuitem action='toggle-big-transport'/>
+      <menuitem action='toggle-transport-masters'/>
 #if 0
       <menuitem action='toggle-speaker-config'/>
 #endif
index 317c5d8d13bbeade6d5cbdbf8942fd27d641bf16..2a24685b14f9ea5ec7ded74e58758c2d3c12a82a 100644 (file)
 #include "ardour/session_state_utils.h"
 #include "ardour/session_utils.h"
 #include "ardour/source_factory.h"
-#include "ardour/slave.h"
+#include "ardour/transport_master.h"
+#include "ardour/transport_master_manager.h"
 #include "ardour/system_exec.h"
 #include "ardour/track.h"
 #include "ardour/vca_manager.h"
@@ -185,6 +186,7 @@ typedef uint64_t microseconds_t;
 #include "time_axis_view_item.h"
 #include "time_info_box.h"
 #include "timers.h"
+#include "transport_masters_dialog.h"
 #include "utils.h"
 #include "utils_videotl.h"
 #include "video_server_dialog.h"
@@ -314,6 +316,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , export_video_dialog (X_("video-export"), _("Video Export Dialog"))
        , lua_script_window (X_("script-manager"), _("Script Manager"))
        , idleometer (X_("idle-o-meter"), _("Idle'o'Meter"))
+       , transport_masters_dialog (X_("transport-masters"), _("Transport Masters"))
        , session_option_editor (X_("session-options-editor"), _("Properties"), boost::bind (&ARDOUR_UI::create_session_option_editor, this))
        , add_video_dialog (X_("add-video"), _("Add Video"), boost::bind (&ARDOUR_UI::create_add_video_dialog, this))
        , bundle_manager (X_("bundle-manager"), _("Bundle Manager"), boost::bind (&ARDOUR_UI::create_bundle_manager, this))
@@ -473,6 +476,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
                export_video_dialog.set_state (*ui_xml, 0);
                lua_script_window.set_state (*ui_xml, 0);
                idleometer.set_state (*ui_xml, 0);
+               transport_masters_dialog.set_state (*ui_xml, 0);
        }
 
        /* Separate windows */
@@ -494,6 +498,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        WM::Manager::instance().register_window (&audio_port_matrix);
        WM::Manager::instance().register_window (&midi_port_matrix);
        WM::Manager::instance().register_window (&idleometer);
+       WM::Manager::instance().register_window (&transport_masters_dialog);
 
        /* do not retain position for add route dialog */
        add_route_dialog.set_state_mask (WindowProxy::Size);
@@ -1801,11 +1806,11 @@ ARDOUR_UI::update_timecode_format ()
 
        if (_session) {
                bool matching;
-               TimecodeSlave* tcslave;
-               SyncSource sync_src = Config->get_sync_source();
+               boost::shared_ptr<TimecodeTransportMaster> tcmaster;
+               boost::shared_ptr<TransportMaster> tm = TransportMasterManager::instance().current();
 
-               if ((sync_src == LTC || sync_src == MTC) && (tcslave = dynamic_cast<TimecodeSlave*>(_session->slave())) != 0) {
-                       matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format());
+               if ((tm->type() == LTC || tm->type() == MTC) && (tcmaster = boost::dynamic_pointer_cast<TimecodeTransportMaster>(tm)) != 0) {
+                       matching = (tcmaster->apparent_timecode_format() == _session->config.get_timecode_format());
                } else {
                        matching = true;
                }
index 0cf7c58815a3691706a019c7fd1ed1a974296e5a..a0b0b078b127b2bd0ba3cde4ca5328e184b9622c 100644 (file)
 #include "route_params_ui.h"
 #include "session_option_editor.h"
 #include "speaker_dialog.h"
+#include "transport_masters_dialog.h"
 #else
 class About;
 class AddRouteDialog;
@@ -119,6 +120,7 @@ class SessionOptionEditor;
 class SpeakerDialog;
 class GlobalPortMatrixWindow;
 class IdleOMeter;
+class TransportMastersDialog;
 #endif
 
 class VideoTimeLine;
@@ -680,6 +682,7 @@ private:
        WM::Proxy<ExportVideoDialog> export_video_dialog;
        WM::Proxy<LuaScriptManager> lua_script_window;
        WM::Proxy<IdleOMeter> idleometer;
+       WM::Proxy<TransportMastersDialog> transport_masters_dialog;
 
        /* Windows/Dialogs that require a creator method */
 
index 931e7a5a504470f429f7ad1c58647d94cf747ecd..b91470a1457a63f5d0d4f042bf924d3b2b627fe8 100644 (file)
@@ -72,6 +72,7 @@
 #include "sfdb_ui.h"
 #include "time_info_box.h"
 #include "timers.h"
+#include "transport_masters_dialog.h"
 
 #include "pbd/i18n.h"
 
@@ -127,6 +128,8 @@ ARDOUR_UI::set_session (Session *s)
        big_clock->set_session (s);
        video_timeline->set_session (s);
        lua_script_window->set_session (s);
+       transport_masters_dialog->set_session (s);
+       rc_option_editor->set_session (s);
 
        /* sensitize menu bar options that are now valid */
 
index 15d9d560c535121d733b05fa3b6abfff4eddc2ce..3fec8c0be42df16da9ee40f062d4d56466ff536f 100644 (file)
@@ -68,7 +68,7 @@ when the pull up/down setting is non-zero."));
                 * This is a UI limitation, imposed by audio-clock and
                 * status displays which combine RC-config & session-properties.
                 *
-                * Notficy RCOptionEditor by emitting a signal if the active
+                * Notify RCOptionEditor by emitting a signal if the active
                 * status changed:
                 */
                Config->ParameterChanged("sync-source");
@@ -349,6 +349,8 @@ ARDOUR_UI::parameter_changed (std::string p)
 {
        if (p == "external-sync") {
 
+               /* session parameter */
+
                ActionManager::map_some_state ("Transport", "ToggleExternalSync", sigc::mem_fun (_session->config, &SessionConfiguration::get_external_sync));
 
                if (!_session->config.get_external_sync()) {
@@ -357,19 +359,27 @@ ARDOUR_UI::parameter_changed (std::string p)
                        ActionManager::get_action ("Transport", "ToggleAutoReturn")->set_sensitive (true);
                        ActionManager::get_action ("Transport", "ToggleFollowEdits")->set_sensitive (true);
                } else {
-                       sync_button.set_text (sync_source_to_string (Config->get_sync_source(), true));
-                       if (_session && _session->locations()->auto_loop_location()) {
-                               // disable looping with external sync.
-                               // This is not necessary because session-transport ignores the loop-state,
-                               // but makes it clear to the user that it's disabled.
-                               _session->request_play_loop (false, false);
-                       }
                        /* XXX we need to make sure that auto-play is off as well as insensitive */
                        ActionManager::get_action ("Transport", "ToggleAutoPlay")->set_sensitive (false);
                        ActionManager::get_action ("Transport", "ToggleAutoReturn")->set_sensitive (false);
                        ActionManager::get_action ("Transport", "ToggleFollowEdits")->set_sensitive (false);
                }
 
+       } else if (p == "sync-source") {
+
+               /* app parameter (RC config) */
+
+               if (_session) {
+                       if (!_session->config.get_external_sync()) {
+                               sync_button.set_text (S_("SyncSource|Int."));
+                       } else {
+                               sync_button.set_text (sync_source_to_string (Config->get_sync_source(), true));
+                       }
+               } else {
+                       /* changing sync source without a session is unlikely/impossible , except during startup */
+                       sync_button.set_text (sync_source_to_string (Config->get_sync_source(), true));
+               }
+
        } else if (p == "follow-edits") {
 
                ActionManager::map_some_state ("Transport", "ToggleFollowEdits", &UIConfiguration::get_follow_edits);
@@ -598,4 +608,3 @@ ARDOUR_UI::synchronize_sync_source_and_video_pullup ()
        }
 
 }
-
index 6a7733ae5f9bd7bc3f9b52116e900a26b2600e5f..9bc7128ab2cbeb31f3b2f618fc7ab751a58ca85a 100644 (file)
@@ -34,7 +34,7 @@
 #include "ardour/profile.h"
 #include "ardour/lmath.h"
 #include "ardour/session.h"
-#include "ardour/slave.h"
+#include "ardour/transport_master.h"
 #include "ardour/tempo.h"
 #include "ardour/types.h"
 
@@ -938,20 +938,21 @@ AudioClock::set_slave_info ()
                return;
        }
 
-       SyncSource sync_src = Config->get_sync_source();
+       const SyncSource sync_src = Config->get_sync_source();
 
-       if (_session->config.get_external_sync()) {
-               Slave* slave = _session->slave();
+       if (_session->transport_master_is_external()) {
 
-               switch (sync_src) {
+               boost::shared_ptr<TransportMaster> tm = _session->transport_master();
+
+               switch (tm->type()) {
                case Engine:
-                       _left_btn.set_text (sync_source_to_string (sync_src, true), true);
+                       _left_btn.set_text (tm->name(), true);
                        _right_btn.set_text ("", true);
                        break;
                case MIDIClock:
-                       if (slave) {
-                               _left_btn.set_text (sync_source_to_string (sync_src, true), true);
-                               _right_btn.set_text (slave->approximate_current_delta (), true);
+                       if (tm) {
+                               _left_btn.set_text (sync_source_to_string (tm->type(), true), true);
+                               _right_btn.set_text (tm->delta_string (), true);
                        } else {
                                _left_btn.set_text (_("--pending--"), true);
                                _right_btn.set_text ("", true);
@@ -959,17 +960,17 @@ AudioClock::set_slave_info ()
                        break;
                case LTC:
                case MTC:
-                       if (slave) {
+                       if (tm) {
                                bool matching;
-                               TimecodeSlave* tcslave;
-                               if ((tcslave = dynamic_cast<TimecodeSlave*>(_session->slave())) != 0) {
-                                       matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format());
+                               boost::shared_ptr<TimecodeTransportMaster> tcmaster;
+                               if ((tcmaster = boost::dynamic_pointer_cast<TimecodeTransportMaster>(tm)) != 0) {
+                                       matching = (tcmaster->apparent_timecode_format() == _session->config.get_timecode_format());
                                        _left_btn.set_text (string_compose ("%1<span face=\"monospace\" foreground=\"%3\">%2</span>",
-                                                               sync_source_to_string(sync_src, true)[0],
-                                                               dynamic_cast<TimecodeSlave*>(slave)->approximate_current_position (),
-                                                               matching ? "#66ff66" : "#ff3333"
+                                                                           sync_source_to_string(tm->type(), true)[0],
+                                                                           tcmaster->position_string (),
+                                                                           matching ? "#66ff66" : "#ff3333"
                                                                ), true);
-                                       _right_btn.set_text (slave->approximate_current_delta (), true);
+                                       _right_btn.set_text (tm->delta_string (), true);
                                }
                        } else {
                                _left_btn.set_text (_("--pending--"), true);
@@ -978,8 +979,7 @@ AudioClock::set_slave_info ()
                        break;
                }
        } else {
-               _left_btn.set_text (string_compose ("%1/%2",
-                                       _("INT"), sync_source_to_string(sync_src, true)), true);
+               _left_btn.set_text (string_compose ("%1/%2", _("INT"), sync_source_to_string (sync_src, true)), true);
                _right_btn.set_text ("", true);
        }
 }
index df99afbad81115fa0c71499100ed2b444d650ae0..7750d65caf53a436297e2cfd5aaa503eb972530d 100644 (file)
@@ -46,6 +46,7 @@ MidiTracer::MidiTracer ()
        , line_count_adjustment (200, 1, 2000, 1, 10)
        , line_count_spinner (line_count_adjustment)
        , line_count_label (_("Line history: "))
+       , _last_receipt (0)
        , autoscroll (true)
        , show_hex (true)
        , show_delta_time (false)
@@ -60,9 +61,6 @@ MidiTracer::MidiTracer ()
        ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect
                (_manager_connection, invalidator (*this), boost::bind (&MidiTracer::ports_changed, this), gui_context());
 
-       _last_receipt.tv_sec = 0;
-       _last_receipt.tv_usec = 0;
-
        VBox* vbox = manage (new VBox);
        vbox->set_spacing (4);
 
@@ -121,7 +119,6 @@ MidiTracer::MidiTracer ()
        port_changed ();
 }
 
-
 MidiTracer::~MidiTracer()
 {
 }
@@ -178,13 +175,13 @@ MidiTracer::port_changed ()
                boost::shared_ptr<ARDOUR::MidiPort> mp = boost::dynamic_pointer_cast<ARDOUR::MidiPort> (p);
 
                if (mp) {
-                       mp->self_parser().any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
-                       mp->set_trace_on (true);
+                       my_parser.any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4));
+                       mp->set_trace (&my_parser);
                        traced_port = mp;
                }
 
        } else {
-               async->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
+               async->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4));
        }
 }
 
@@ -194,40 +191,33 @@ MidiTracer::disconnect ()
        _parser_connection.disconnect ();
 
        if (traced_port) {
-               traced_port->set_trace_on (false);
+               traced_port->set_trace (0);
                traced_port.reset ();
        }
 }
 
 void
-MidiTracer::tracer (Parser&, byte* msg, size_t len)
+MidiTracer::tracer (Parser&, byte* msg, size_t len, samplecnt_t now)
 {
        stringstream ss;
-       struct timeval tv;
        char* buf;
-       struct tm now;
        size_t bufsize;
        size_t s;
 
-       gettimeofday (&tv, 0);
+       std::cerr << "tracer msg " << len << " bytes, first = " << hex << (int) msg[0] << dec << std::endl;
 
        buf = (char *) buffer_pool.alloc ();
        bufsize = buffer_size;
 
-       if (_last_receipt.tv_sec != 0 && show_delta_time) {
-               struct timeval delta;
-               timersub (&tv, &_last_receipt, &delta);
-               s = snprintf (buf, bufsize, "+%02" PRId64 ":%06" PRId64, (int64_t) delta.tv_sec, (int64_t) delta.tv_usec);
+       if (_last_receipt != 0 && show_delta_time) {
+               s = snprintf (buf, bufsize, "+%12ld", now - _last_receipt);
                bufsize -= s;
        } else {
-               localtime_r ((const time_t*)&tv.tv_sec, &now);
-               s = strftime (buf, bufsize, "%H:%M:%S", &now);
-               bufsize -= s;
-               s += snprintf (&buf[s], bufsize, ".%06" PRId64, (int64_t) tv.tv_usec);
+               s = snprintf (buf, bufsize, "%12ld", now);
                bufsize -= s;
        }
 
-       _last_receipt = tv;
+       _last_receipt = now;
 
        switch ((eventType) msg[0]&0xf0) {
        case off:
index a221152cd7470e14629fd6910370246e4bb44a24..83e99ef979bf977d2f2db0ec1105b5dc27440787 100644 (file)
@@ -56,7 +56,7 @@ private:
        Gtk::SpinButton line_count_spinner;
        Gtk::Label line_count_label;
        Gtk::HBox line_count_box;
-       struct timeval _last_receipt;
+       MIDI::samplecnt_t _last_receipt;
 
        bool autoscroll;
        bool show_hex;
@@ -72,7 +72,7 @@ private:
        Pool buffer_pool;
        static const size_t buffer_size = 256;
 
-       void tracer (MIDI::Parser&, MIDI::byte*, size_t);
+       void tracer (MIDI::Parser&, MIDI::byte*, size_t, MIDI::samplecnt_t);
        void update ();
 
        Gtk::CheckButton autoscroll_button;
@@ -91,6 +91,7 @@ private:
        void disconnect ();
        PBD::ScopedConnection _parser_connection;
        PBD::ScopedConnection _manager_connection;
+       MIDI::Parser my_parser;
 
        boost::shared_ptr<ARDOUR::MidiPort> traced_port;
 };
index 97912eab4cb2bddb506f6fabd8d9530ca9d5c43c..b685e789472c35f5e3e0455c7bf67509500dde34 100644 (file)
@@ -608,6 +608,20 @@ ClockOption::set_session (Session* s)
 
 /*--------------------------*/
 
+WidgetOption::WidgetOption (string const & i, string const & n, Gtk::Widget& w)
+       : Option (i, n)
+       , _widget (&w)
+{
+}
+
+void
+WidgetOption::add_to_page (OptionEditorPage* p)
+{
+       add_widget_to_page (p, _widget);
+}
+
+/*--------------------------*/
+
 OptionEditorPage::OptionEditorPage ()
        : table (1, 3)
 {
index 055ee9b8ebebf8e1f26ffaa2b5f3ecbe1fcf2d6a..132d260c05fbb4685decf2b593399593ca260bb1 100644 (file)
@@ -558,6 +558,21 @@ private:
        sigc::slot<bool, ARDOUR::gain_t> _set;
 };
 
+class WidgetOption : public Option
+{
+  public:
+       WidgetOption (std::string const & i, std::string const & n, Gtk::Widget& w);
+
+       void add_to_page (OptionEditorPage*);
+       void parameter_changed (std::string const &) {}
+       void set_state_from_config () {}
+
+       Gtk::Widget& tip_widget() { return *_widget; }
+
+  private:
+       Gtk::Widget* _widget;
+};
+
 class ClockOption : public Option
 {
 public:
index 63291498e12d1d868b5876bcb6cff1ce2b7b8935..12af044d0b17fa8d2baf2e72c31ea13d79da7699 100644 (file)
@@ -436,9 +436,10 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
                        ltc->add_channel (_("LTC Out"), DataType::AUDIO, session->engine().make_port_name_non_relative (session->ltc_output_port()->name()));
                        program->add_bundle (ltc);
                } else {
-                       boost::shared_ptr<Bundle> ltc (new Bundle (_("LTC In"), inputs));
-                       ltc->add_channel (_("LTC In"), DataType::AUDIO, session->engine().make_port_name_non_relative (session->ltc_input_port()->name()));
-                       program->add_bundle (ltc);
+                       // XXX TRANSPORTMASTERS
+                       //boost::shared_ptr<Bundle> ltc (new Bundle (_("LTC In"), inputs));
+                       // ltc->add_channel (_("LTC In"), DataType::AUDIO, session->engine().make_port_name_non_relative (session->ltc_input_port()->name()));
+                       // program->add_bundle (ltc);
                }
        }
 
@@ -470,12 +471,13 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
                AudioEngine* ae = AudioEngine::instance();
 
                if (inputs) {
-                       sync->add_channel (
-                               _("MTC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mtc_input_port()->name())
-                               );
-                       sync->add_channel (
-                               _("MIDI clock in"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_clock_input_port()->name())
-                               );
+                       // XXX TRANSPORTMASTER
+                       // sync->add_channel (
+                       // _("MTC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mtc_input_port()->name())
+                       // );
+                       // sync->add_channel (
+                       // _("MIDI clock in"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_clock_input_port()->name())
+                       //);
                        sync->add_channel (
                                _("MMC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mmc_input_port()->name())
                                );
index 8cca0227f84c62e02607e091903ec4d889d1b4ea..4208e69247e2e107fa26b7459a991b26d262e4aa 100644 (file)
 
 #include "ardour/audio_backend.h"
 #include "ardour/audioengine.h"
-#include "ardour/profile.h"
-#include "ardour/dB.h"
-#include "ardour/rc_configuration.h"
 #include "ardour/control_protocol_manager.h"
+#include "ardour/dB.h"
 #include "ardour/port_manager.h"
 #include "ardour/plugin_manager.h"
+#include "ardour/profile.h"
+#include "ardour/rc_configuration.h"
+#include "ardour/transport_master_manager.h"
+
 #include "control_protocol/control_protocol.h"
 
 #include "waveview/wave_view.h"
@@ -66,6 +68,7 @@
 #include "midi_tracer.h"
 #include "rc_option_editor.h"
 #include "sfdb_ui.h"
+#include "transport_masters_dialog.h"
 #include "ui_config.h"
 #include "utils.h"
 
@@ -2109,14 +2112,7 @@ MidiPortOptions::pretty_name_edit (std::string const & path, string const & new_
                return;
        }
 
-       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-       if (backend) {
-               ARDOUR::PortEngine::PortHandle ph = backend->get_port_by_name ((*iter)[midi_port_columns.name]);
-               if (ph) {
-                       backend->set_port_property (ph, "http://jackaudio.org/metadata/pretty-name", new_text, "");
-                       (*iter)[midi_port_columns.pretty_name] = new_text;
-               }
-       }
+       AudioEngine::instance()->set_midi_port_pretty_name ((*iter)[midi_port_columns.name], new_text);
 }
 
 /*============*/
@@ -3213,16 +3209,9 @@ RCOptionEditor::RCOptionEditor ()
 
        /* SYNC */
 
-       add_option (_("Sync"), new OptionEditorHeading (_("External Synchronization")));
-
-       _sync_source = new ComboOption<SyncSource> (
-               "sync-source",
-               _("External timecode source"),
-               sigc::mem_fun (*_rc_config, &RCConfiguration::get_sync_source),
-               sigc::mem_fun (*_rc_config, &RCConfiguration::set_sync_source)
-               );
+       add_option (_("Sync"), new OptionEditorHeading (_("Transport Masters")));
 
-       add_option (_("Sync"), _sync_source);
+       add_option (_("Sync"), new WidgetOption (X_("foo"), X_("Transport Masters"), _transport_masters_widget));
 
        _sync_framerate = new BoolOption (
                     "timecode-sync-frame-rate",
@@ -3240,45 +3229,6 @@ RCOptionEditor::RCOptionEditor ()
 
        add_option (_("Sync"), _sync_framerate);
 
-       _sync_genlock = new BoolOption (
-               "timecode-source-is-synced",
-               _("Sync-lock timecode to clock (disable drift compensation)"),
-               sigc::mem_fun (*_rc_config, &RCConfiguration::get_timecode_source_is_synced),
-               sigc::mem_fun (*_rc_config, &RCConfiguration::set_timecode_source_is_synced)
-               );
-       Gtkmm2ext::UI::instance()->set_tip
-               (_sync_genlock->tip_widget(),
-                string_compose (_("<b>When enabled</b> %1 will never varispeed when slaved to external timecode. "
-                                  "Sync Lock indicates that the selected external timecode source shares clock-sync "
-                                  "(Black &amp; Burst, Wordclock, etc) with the audio interface. "
-                                  "This option disables drift compensation. The transport speed is fixed at 1.0. "
-                                  "Vari-speed LTC will be ignored and cause drift."
-                                  "\n\n"
-                                  "<b>When disabled</b> %1 will compensate for potential drift, regardless if the "
-                                  "timecode sources shares clock sync."
-                                 ), PROGRAM_NAME));
-
-
-       add_option (_("Sync"), _sync_genlock);
-
-       _sync_source_2997 = new BoolOption (
-               "timecode-source-2997",
-               _("Lock to 29.9700 fps instead of 30000/1001"),
-               sigc::mem_fun (*_rc_config, &RCConfiguration::get_timecode_source_2997),
-               sigc::mem_fun (*_rc_config, &RCConfiguration::set_timecode_source_2997)
-               );
-       Gtkmm2ext::UI::instance()->set_tip
-               (_sync_source_2997->tip_widget(),
-                _("<b>When enabled</b> the external timecode source is assumed to use 29.97 fps instead of 30000/1001.\n"
-                        "SMPTE 12M-1999 specifies 29.97df as 30000/1001. The spec further mentions that "
-                        "drop-sample timecode has an accumulated error of -86ms over a 24-hour period.\n"
-                        "Drop-sample timecode would compensate exactly for a NTSC color frame rate of 30 * 0.9990 (ie 29.970000). "
-                        "That is not the actual rate. However, some vendors use that rate - despite it being against the specs - "
-                        "because the variant of using exactly 29.97 fps has zero timecode drift.\n"
-                        ));
-
-       add_option (_("Sync"), _sync_source_2997);
-
        add_option (_("Sync/LTC"), new OptionEditorHeading (_("Linear Timecode (LTC) Reader")));
 
        _ltc_port = new ComboStringOption (
@@ -3293,9 +3243,6 @@ RCOptionEditor::RCOptionEditor ()
        AudioEngine::instance()->get_physical_inputs (DataType::AUDIO, physical_inputs);
        _ltc_port->set_popdown_strings (physical_inputs);
 
-       populate_sync_options ();
-       AudioEngine::instance()->Running.connect (engine_started_connection, MISSING_INVALIDATOR, boost::bind (&RCOptionEditor::populate_sync_options, this), gui_context());
-
        add_option (_("Sync/LTC"), _ltc_port);
 
        add_option (_("Sync/LTC"), new OptionEditorHeading (_("Linear Timecode (LTC) Generator")));
@@ -4058,6 +4005,13 @@ These settings will only take effect after %1 is restarted.\n\
        set_current_page (_("General"));
 }
 
+void
+RCOptionEditor::set_session (Session *s)
+{
+       SessionHandlePtr::set_session (s);
+       _transport_masters_widget.set_session (s);
+}
+
 void
 RCOptionEditor::parameter_changed (string const & p)
 {
@@ -4072,22 +4026,11 @@ RCOptionEditor::parameter_changed (string const & p)
                _solo_control_is_listen_control->set_sensitive (s);
                _listen_position->set_sensitive (s);
        } else if (p == "sync-source") {
-               _sync_source->set_sensitive (true);
-               if (_session) {
-                       _sync_source->set_sensitive (!_session->config.get_external_sync());
-               }
-               switch(Config->get_sync_source()) {
-               case ARDOUR::MTC:
-               case ARDOUR::LTC:
-                       _sync_genlock->set_sensitive (true);
+               boost::shared_ptr<TransportMaster> tm (TransportMasterManager::instance().current());
+               if (boost::dynamic_pointer_cast<TimecodeTransportMaster> (tm)) {
                        _sync_framerate->set_sensitive (true);
-                       _sync_source_2997->set_sensitive (true);
-                       break;
-               default:
-                       _sync_genlock->set_sensitive (false);
+               } else {
                        _sync_framerate->set_sensitive (false);
-                       _sync_source_2997->set_sensitive (false);
-                       break;
                }
        } else if (p == "send-ltc") {
                bool const s = Config->get_send_ltc ();
@@ -4164,29 +4107,6 @@ void RCOptionEditor::edit_vst_path () {
        delete pd;
 }
 
-
-void
-RCOptionEditor::populate_sync_options ()
-{
-       vector<SyncSource> sync_opts = ARDOUR::get_available_sync_options ();
-
-       _sync_source->clear ();
-
-       for (vector<SyncSource>::iterator i = sync_opts.begin(); i != sync_opts.end(); ++i) {
-               _sync_source->add (*i, sync_source_to_string (*i));
-       }
-
-       if (sync_opts.empty()) {
-               _sync_source->set_sensitive(false);
-       } else {
-               if (std::find(sync_opts.begin(), sync_opts.end(), _rc_config->get_sync_source()) == sync_opts.end()) {
-                       _rc_config->set_sync_source(sync_opts.front());
-               }
-       }
-
-       parameter_changed ("sync-source");
-}
-
 Gtk::Window*
 RCOptionEditor::use_own_window (bool and_fill_it)
 {
index 3a3283e86c7d2e923c95ed12aa3f8c2a4c0b1b26..27d261ba09f2fcb49015f4b2436d20bb307144d6 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "option_editor.h"
 #include "visibility_group.h"
+#include "transport_masters_dialog.h"
 
 /** @file rc_option_editor.h
  *  @brief Editing of options which are obtained from and written back to one of the .rc files.
@@ -39,7 +40,7 @@ class RCOptionEditor : public OptionEditorContainer, public ARDOUR::SessionHandl
 public:
        RCOptionEditor ();
 
-       void populate_sync_options ();
+       void set_session (ARDOUR::Session*);
 
        Gtk::Window* use_own_window (bool and_fill_it);
        XMLNode& get_state ();
@@ -53,13 +54,12 @@ private:
        VisibilityGroup _mixer_strip_visibility;
        ComboOption<ARDOUR::SyncSource>* _sync_source;
        BoolOption* _sync_framerate;
-       BoolOption* _sync_genlock;
-       BoolOption* _sync_source_2997;
        ComboStringOption* _ltc_port;
        HSliderOption* _ltc_volume_slider;
        Gtk::Adjustment* _ltc_volume_adjustment;
        BoolOption* _ltc_send_continuously;
        BoolOption* _plugin_prefer_inline;
+       TransportMastersWidget _transport_masters_widget;
 
        PBD::ScopedConnection parameter_change_connection;
        PBD::ScopedConnection engine_started_connection;
diff --git a/gtk2_ardour/transport_masters_dialog.cc b/gtk2_ardour/transport_masters_dialog.cc
new file mode 100644 (file)
index 0000000..527717c
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+    Copyright (C) 2018 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "pbd/enumwriter.h"
+#include "pbd/i18n.h"
+
+#include "temporal/time.h"
+
+#include "ardour/audioengine.h"
+#include "ardour/session.h"
+#include "ardour/transport_master.h"
+#include "ardour/transport_master_manager.h"
+
+#include "widgets/tooltips.h"
+
+#include "gtkmm2ext/utils.h"
+#include "gtkmm2ext/gui_thread.h"
+
+#include "ardour_ui.h"
+#include "transport_masters_dialog.h"
+
+using namespace std;
+using namespace Gtk;
+using namespace Gtkmm2ext;
+using namespace ARDOUR;
+using namespace PBD;
+using namespace ArdourWidgets;
+
+TransportMastersWidget::TransportMastersWidget ()
+       : table (4, 9)
+{
+       pack_start (table, PACK_EXPAND_WIDGET, 12);
+
+       col_title[0].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Name")));
+       col_title[0].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Name")));
+       col_title[1].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Type")));
+       col_title[2].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Format")));
+       col_title[3].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Current")));
+       col_title[4].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Timestamp")));
+       col_title[5].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Delta")));
+       col_title[6].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Collect")));
+       col_title[7].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Use")));
+       col_title[8].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Data Source")));
+       col_title[9].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Accept")));
+       col_title[10].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Clock Synced")));
+       col_title[11].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("29.97/30")));
+
+       set_tooltip (col_title[11], _("<b>When enabled</b> the external timecode source is assumed to use 29.97 fps instead of 30000/1001.\n"
+                                    "SMPTE 12M-1999 specifies 29.97df as 30000/1001. The spec further mentions that "
+                                    "drop-sample timecode has an accumulated error of -86ms over a 24-hour period.\n"
+                                    "Drop-sample timecode would compensate exactly for a NTSC color frame rate of 30 * 0.9990 (ie 29.970000). "
+                                    "That is not the actual rate. However, some vendors use that rate - despite it being against the specs - "
+                                    "because the variant of using exactly 29.97 fps has zero timecode drift.\n"
+                            ));
+
+
+       table.set_spacings (6);
+
+       TransportMasterManager::instance().CurrentChanged.connect (current_connection, invalidator (*this), boost::bind (&TransportMastersWidget::current_changed, this, _1, _2), gui_context());
+
+       rebuild ();
+}
+
+TransportMastersWidget::~TransportMastersWidget ()
+{
+       for (vector<Row*>::iterator r = rows.begin(); r != rows.end(); ++r) {
+               delete *r;
+       }
+}
+
+void
+TransportMastersWidget::current_changed (boost::shared_ptr<TransportMaster> old_master, boost::shared_ptr<TransportMaster> new_master)
+{
+       cerr << "master changed to " << new_master << endl;
+}
+
+void
+TransportMastersWidget::rebuild ()
+{
+       TransportMasterManager::TransportMasters const & masters (TransportMasterManager::instance().transport_masters());
+
+       container_clear (table);
+
+       for (vector<Row*>::iterator r = rows.begin(); r != rows.end(); ++r) {
+               delete *r;
+       }
+
+       rows.clear ();
+       table.resize (masters.size()+1, 12);
+
+       table.attach (col_title[0], 0, 1, 0, 1);
+       table.attach (col_title[1], 1, 2, 0, 1);
+       table.attach (col_title[2], 2, 3, 0, 1);
+       table.attach (col_title[3], 3, 4, 0, 1);
+       table.attach (col_title[4], 4, 5, 0, 1);
+       table.attach (col_title[5], 5, 6, 0, 1);
+       table.attach (col_title[6], 6, 7, 0, 1);
+       table.attach (col_title[7], 7, 8, 0, 1);
+       table.attach (col_title[8], 8, 9, 0, 1);
+       table.attach (col_title[9], 9, 10, 0, 1);
+       table.attach (col_title[10], 10, 11, 0, 1);
+       table.attach (col_title[11], 11, 12, 0, 1);
+
+       uint32_t n = 1;
+
+       for (TransportMasterManager::TransportMasters::const_iterator m = masters.begin(); m != masters.end(); ++m, ++n) {
+
+               Row* r = new Row;
+               rows.push_back (r);
+
+               r->tm = *m;
+               r->label.set_text ((*m)->name());
+               r->type.set_text (enum_2_string  ((*m)->type()));
+
+               r->use_button.set_group (use_button_group);
+
+               if (TransportMasterManager::instance().current() == r->tm) {
+                       r->use_button.set_active (true);
+               }
+
+               table.attach (r->type, 0, 1, n, n+1);
+               table.attach (r->label, 1, 2, n, n+1);
+               table.attach (r->format, 2, 3, n, n+1);
+               table.attach (r->current, 3, 4, n, n+1);
+               table.attach (r->timestamp, 4, 5, n, n+1);
+               table.attach (r->delta, 5, 6, n, n+1);
+               table.attach (r->collect_button, 6, 7, n, n+1);
+               table.attach (r->use_button, 7, 8, n, n+1);
+               table.attach (r->port_combo, 8, 9, n, n+1);
+               table.attach (r->request_options, 9, 10, n, n+1);
+
+               if (boost::dynamic_pointer_cast<TimecodeTransportMaster> (r->tm)) {
+                       table.attach (r->sclock_synced_button, 10, 11, n, n+1);
+                       r->sclock_synced_button.set_active (r->tm->sample_clock_synced());
+                       r->sclock_synced_button.signal_toggled().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::sync_button_toggled));
+                       table.attach (r->fps_299730_button, 11, 12, n, n+1);
+               }
+
+               r->port_combo.signal_changed().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::port_choice_changed));
+               ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (*r, invalidator (*this), boost::bind (&TransportMastersWidget::Row::connection_handler, r), gui_context());
+
+               r->collect_button.set_active (r->tm->collect());
+
+               r->use_button.signal_toggled().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::use_button_toggled));
+               r->collect_button.signal_toggled().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::collect_button_toggled));
+               r->request_options.signal_button_press_event().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::request_option_press), false);
+
+       }
+}
+
+TransportMastersWidget::Row::Row ()
+       : request_option_menu (0)
+       , ignore_active_change (false)
+{
+}
+
+void
+TransportMastersWidget::Row::use_button_toggled ()
+{
+       if (use_button.get_active()) {
+               Config->set_sync_source (tm->type());
+       }
+}
+
+void
+TransportMastersWidget::Row::collect_button_toggled ()
+{
+       tm->set_collect (collect_button.get_active());
+}
+
+void
+TransportMastersWidget::Row::sync_button_toggled ()
+{
+       tm->set_sample_clock_synced (sclock_synced_button.get_active());
+}
+
+bool
+TransportMastersWidget::Row::request_option_press (GdkEventButton* ev)
+{
+       if (ev->button == 1) {
+               if (!request_option_menu) {
+                       build_request_options ();
+               }
+               request_option_menu->popup (1, ev->time);
+               return true;
+       }
+       return false;
+}
+
+void
+TransportMastersWidget::Row::build_request_options ()
+{
+       using namespace Gtk::Menu_Helpers;
+
+       request_option_menu = manage (new Menu);
+
+       MenuList& items (request_option_menu->items());
+
+       items.push_back (CheckMenuElem (_("Accept speed-changing commands (start/stop)")));
+       CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back ());
+       i->set_active (tm->request_mask() & TR_Speed);
+       items.push_back (CheckMenuElem (_("Accept locate commands")));
+       i = dynamic_cast<CheckMenuItem *> (&items.back ());
+       i->set_active (tm->request_mask() & TR_Locate);
+}
+
+void
+TransportMastersWidget::Row::connection_handler ()
+{
+}
+
+Glib::RefPtr<Gtk::ListStore>
+TransportMastersWidget::Row::build_port_list (vector<string> const & ports)
+{
+       Glib::RefPtr<Gtk::ListStore> store = ListStore::create (port_columns);
+       TreeModel::Row row;
+
+       row = *store->append ();
+       row[port_columns.full_name] = string();
+       row[port_columns.short_name] = _("Disconnected");
+
+       for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
+
+               if (AudioEngine::instance()->port_is_mine (*p)) {
+                       continue;
+               }
+
+               row = *store->append ();
+               row[port_columns.full_name] = *p;
+
+               std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
+               if (pn.empty ()) {
+                       pn = (*p).substr ((*p).find (':') + 1);
+               }
+               row[port_columns.short_name] = pn;
+       }
+
+       return store;
+}
+
+void
+TransportMastersWidget::Row::populate_port_combo ()
+{
+       if (!tm->port()) {
+               port_combo.hide ();
+               return;
+       } else {
+               port_combo.show ();
+       }
+
+       vector<string> inputs;
+
+       if (tm->port()->type() == DataType::MIDI) {
+               ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput), inputs);
+       } else {
+               ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::AUDIO, ARDOUR::PortFlags (ARDOUR::IsOutput), inputs);
+       }
+
+       Glib::RefPtr<Gtk::ListStore> input = build_port_list (inputs);
+       bool input_found = false;
+       int n;
+
+       port_combo.set_model (input);
+
+       Gtk::TreeModel::Children children = input->children();
+       Gtk::TreeModel::Children::iterator i;
+       i = children.begin();
+       ++i; /* skip "Disconnected" */
+
+
+       for (n = 1;  i != children.end(); ++i, ++n) {
+               string port_name = (*i)[port_columns.full_name];
+               if (tm->port()->connected_to (port_name)) {
+                       port_combo.set_active (n);
+                       input_found = true;
+                       break;
+               }
+       }
+
+       if (!input_found) {
+               port_combo.set_active (0); /* disconnected */
+       }
+}
+
+void
+TransportMastersWidget::Row::port_choice_changed ()
+{
+       if (ignore_active_change) {
+               return;
+       }
+
+       TreeModel::iterator active = port_combo.get_active ();
+       string new_port = (*active)[port_columns.full_name];
+
+       if (new_port.empty()) {
+               tm->port()->disconnect_all ();
+               return;
+       }
+
+       if (!tm->port()->connected_to (new_port)) {
+               tm->port()->disconnect_all ();
+               tm->port()->connect (new_port);
+       }
+}
+
+void
+TransportMastersWidget::Row::update (Session* s, samplepos_t now)
+{
+       using namespace Timecode;
+
+       samplepos_t pos;
+       double speed;
+       stringstream ss;
+       Time t;
+       boost::shared_ptr<TimecodeTransportMaster> ttm;
+       boost::shared_ptr<MIDIClock_TransportMaster> mtm;
+
+       if (s) {
+
+               if (tm->speed_and_position (speed, pos, now)) {
+
+                       sample_to_timecode (pos, t, false, false, 25, false, AudioEngine::instance()->sample_rate(), 100, false, 0);
+
+                       if ((ttm = boost::dynamic_pointer_cast<TimecodeTransportMaster> (tm))) {
+                               format.set_text (timecode_format_name (ttm->apparent_timecode_format()));
+                       } else if ((mtm = boost::dynamic_pointer_cast<MIDIClock_TransportMaster> (tm))) {
+                               char buf[8];
+                               snprintf (buf, sizeof (buf), "%.1f", mtm->bpm());
+                               format.set_text (buf);
+                       } else {
+                               format.set_text ("");
+                       }
+                       current.set_text (Timecode::timecode_format_time (t));
+                       timestamp.set_markup (tm->position_string());
+                       delta.set_markup (tm->delta_string ());
+
+               }
+       }
+
+       populate_port_combo ();
+}
+
+void
+TransportMastersWidget::update (samplepos_t audible)
+{
+       samplepos_t now = AudioEngine::instance()->sample_time ();
+
+       for (vector<Row*>::iterator r = rows.begin(); r != rows.end(); ++r) {
+               (*r)->update (_session, now);
+       }
+}
+
+void
+TransportMastersWidget::on_map ()
+{
+       update_connection = ARDOUR_UI::Clock.connect (sigc::mem_fun (*this, &TransportMastersWidget::update));
+       Gtk::VBox::on_map ();
+}
+
+void
+TransportMastersWidget::on_unmap ()
+{
+       update_connection.disconnect ();
+       Gtk::VBox::on_unmap ();
+}
+
+TransportMastersDialog::TransportMastersDialog ()
+       : ArdourDialog (_("Transport Masters"))
+{
+       get_vbox()->pack_start (w);
+       w.show ();
+}
+
+void
+TransportMastersDialog::set_session (ARDOUR::Session* s)
+{
+       ArdourDialog::set_session (s);
+       w.set_session (s);
+}
diff --git a/gtk2_ardour/transport_masters_dialog.h b/gtk2_ardour/transport_masters_dialog.h
new file mode 100644 (file)
index 0000000..838e021
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+    Copyright (C) 2018 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_gtk_transport_masters_dialog_h__
+#define __ardour_gtk_transport_masters_dialog_h__
+
+#include <vector>
+#include <string>
+
+#include <gtkmm/button.h>
+#include <gtkmm/radiobutton.h>
+#include <gtkmm/label.h>
+#include <gtkmm/table.h>
+#include <gtkmm/treestore.h>
+
+#include "ardour_dialog.h"
+
+namespace Gtk {
+       class Menu;
+}
+
+namespace ARDOUR {
+       class TransportMaster;
+}
+
+class TransportMastersWidget : public Gtk::VBox, public ARDOUR::SessionHandlePtr
+{
+  public:
+       TransportMastersWidget ();
+       ~TransportMastersWidget ();
+
+       void update (ARDOUR::samplepos_t);
+
+  protected:
+       void on_map ();
+       void on_unmap ();
+
+  private:
+
+       struct Row : sigc::trackable, PBD::ScopedConnectionList {
+               Gtk::Label label;
+               Gtk::Label type;
+               Gtk::Label format;
+               Gtk::Label current;
+               Gtk::Label timestamp;
+               Gtk::Label delta;
+               Gtk::CheckButton collect_button;
+               Gtk::RadioButton use_button;
+               Gtk::ComboBoxText port_combo;
+               Gtk::CheckButton sclock_synced_button;
+               Gtk::CheckButton fps_299730_button;
+               Gtk::Button request_options;
+               Gtk::Menu* request_option_menu;
+
+               void build_request_options();
+
+               boost::shared_ptr<ARDOUR::TransportMaster> tm;
+
+               void update (ARDOUR::Session*, ARDOUR::samplepos_t);
+
+               Row ();
+
+               struct PortColumns : public Gtk::TreeModel::ColumnRecord {
+                       PortColumns() {
+                               add (short_name);
+                               add (full_name);
+                       }
+                       Gtk::TreeModelColumn<std::string> short_name;
+                       Gtk::TreeModelColumn<std::string> full_name;
+               };
+
+               PortColumns port_columns;
+
+               void populate_port_combo ();
+               Glib::RefPtr<Gtk::ListStore> build_port_list (std::vector<std::string> const & ports);
+
+               void use_button_toggled ();
+               void collect_button_toggled ();
+               void sync_button_toggled ();
+               void port_choice_changed ();
+               void connection_handler ();
+               bool request_option_press (GdkEventButton*);
+
+               bool ignore_active_change;
+       };
+
+       std::vector<Row*> rows;
+
+       Gtk::RadioButtonGroup use_button_group;
+       Gtk::Table table;
+       Gtk::Label col_title[12];
+
+       sigc::connection update_connection;
+       PBD::ScopedConnection current_connection;
+
+       void rebuild ();
+       void current_changed (boost::shared_ptr<ARDOUR::TransportMaster> old_master, boost::shared_ptr<ARDOUR::TransportMaster> new_master);
+
+};
+
+class TransportMastersDialog : public ArdourDialog
+{
+  public:
+       TransportMastersDialog ();
+
+       void set_session (ARDOUR::Session*);
+
+  private:
+       TransportMastersWidget w;
+};
+
+
+#endif /* __ardour_gtk_transport_masters_dialog_h__ */
index ad61135d3268fb62cee1be3d74e92a69bce65f27..aadf185b2a76aaac189cafd985dfdbf782aff30a 100644 (file)
@@ -262,6 +262,7 @@ gtk2_ardour_sources = [
         'transform_dialog.cc',
         'transport_control.cc',
         'transport_control_ui.cc',
+        'transport_masters_dialog.cc',
         'transpose_dialog.cc',
         'ui_config.cc',
         'utils.cc',