From db385c2e3c67875cf8f6dc10ebe8a540aa2ce1ad Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 18 Sep 2018 18:52:20 -0400 Subject: new transport slave/master implementation, gui edition --- gtk2_ardour/ardour.menus.in | 1 + gtk2_ardour/ardour_ui.cc | 15 +- gtk2_ardour/ardour_ui.h | 3 + gtk2_ardour/ardour_ui_dialogs.cc | 3 + gtk2_ardour/ardour_ui_options.cc | 27 ++- gtk2_ardour/audio_clock.cc | 38 +-- gtk2_ardour/midi_tracer.cc | 32 +-- gtk2_ardour/midi_tracer.h | 5 +- gtk2_ardour/option_editor.cc | 14 ++ gtk2_ardour/option_editor.h | 15 ++ gtk2_ardour/port_group.cc | 20 +- gtk2_ardour/rc_option_editor.cc | 118 ++-------- gtk2_ardour/rc_option_editor.h | 6 +- gtk2_ardour/transport_masters_dialog.cc | 395 ++++++++++++++++++++++++++++++++ gtk2_ardour/transport_masters_dialog.h | 129 +++++++++++ gtk2_ardour/wscript | 1 + 16 files changed, 655 insertions(+), 167 deletions(-) create mode 100644 gtk2_ardour/transport_masters_dialog.cc create mode 100644 gtk2_ardour/transport_masters_dialog.h diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index f952a410a7..858af01943 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -556,6 +556,7 @@ #endif + #if 0 #endif diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 317c5d8d13..2a24685b14 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -105,7 +105,8 @@ #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 tcmaster; + boost::shared_ptr tm = TransportMasterManager::instance().current(); - if ((sync_src == LTC || sync_src == MTC) && (tcslave = dynamic_cast(_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(tm)) != 0) { + matching = (tcmaster->apparent_timecode_format() == _session->config.get_timecode_format()); } else { matching = true; } diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 0cf7c58815..a0b0b078b1 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -101,6 +101,7 @@ #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 export_video_dialog; WM::Proxy lua_script_window; WM::Proxy idleometer; + WM::Proxy transport_masters_dialog; /* Windows/Dialogs that require a creator method */ diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index 931e7a5a50..b91470a145 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -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 */ diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index 15d9d560c5..3fec8c0be4 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -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 () } } - diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 6a7733ae5f..9bc7128ab2 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -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 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(_session->slave())) != 0) { - matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format()); + boost::shared_ptr tcmaster; + if ((tcmaster = boost::dynamic_pointer_cast(tm)) != 0) { + matching = (tcmaster->apparent_timecode_format() == _session->config.get_timecode_format()); _left_btn.set_text (string_compose ("%1%2", - sync_source_to_string(sync_src, true)[0], - dynamic_cast(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); } } diff --git a/gtk2_ardour/midi_tracer.cc b/gtk2_ardour/midi_tracer.cc index df99afbad8..7750d65caf 100644 --- a/gtk2_ardour/midi_tracer.cc +++ b/gtk2_ardour/midi_tracer.cc @@ -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 mp = boost::dynamic_pointer_cast (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: diff --git a/gtk2_ardour/midi_tracer.h b/gtk2_ardour/midi_tracer.h index a221152cd7..83e99ef979 100644 --- a/gtk2_ardour/midi_tracer.h +++ b/gtk2_ardour/midi_tracer.h @@ -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 traced_port; }; diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index 97912eab4c..b685e78947 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -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) { diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index 055ee9b8eb..132d260c05 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -558,6 +558,21 @@ private: sigc::slot _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: diff --git a/gtk2_ardour/port_group.cc b/gtk2_ardour/port_group.cc index 63291498e1..12af044d0b 100644 --- a/gtk2_ardour/port_group.cc +++ b/gtk2_ardour/port_group.cc @@ -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 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 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()) ); diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index 8cca0227f8..4208e69247 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -43,12 +43,14 @@ #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 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 ( - "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 (_("When enabled %1 will never varispeed when slaved to external timecode. " - "Sync Lock indicates that the selected external timecode source shares clock-sync " - "(Black & 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" - "When disabled %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(), - _("When enabled 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 tm (TransportMasterManager::instance().current()); + if (boost::dynamic_pointer_cast (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 sync_opts = ARDOUR::get_available_sync_options (); - - _sync_source->clear (); - - for (vector::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) { diff --git a/gtk2_ardour/rc_option_editor.h b/gtk2_ardour/rc_option_editor.h index 3a3283e86c..27d261ba09 100644 --- a/gtk2_ardour/rc_option_editor.h +++ b/gtk2_ardour/rc_option_editor.h @@ -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* _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 index 0000000000..527717c264 --- /dev/null +++ b/gtk2_ardour/transport_masters_dialog.cc @@ -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 ("%1", _("Name"))); + col_title[0].set_markup (string_compose ("%1", _("Name"))); + col_title[1].set_markup (string_compose ("%1", _("Type"))); + col_title[2].set_markup (string_compose ("%1", _("Format"))); + col_title[3].set_markup (string_compose ("%1", _("Current"))); + col_title[4].set_markup (string_compose ("%1", _("Timestamp"))); + col_title[5].set_markup (string_compose ("%1", _("Delta"))); + col_title[6].set_markup (string_compose ("%1", _("Collect"))); + col_title[7].set_markup (string_compose ("%1", _("Use"))); + col_title[8].set_markup (string_compose ("%1", _("Data Source"))); + col_title[9].set_markup (string_compose ("%1", _("Accept"))); + col_title[10].set_markup (string_compose ("%1", _("Clock Synced"))); + col_title[11].set_markup (string_compose ("%1", _("29.97/30"))); + + set_tooltip (col_title[11], _("When enabled 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::iterator r = rows.begin(); r != rows.end(); ++r) { + delete *r; + } +} + +void +TransportMastersWidget::current_changed (boost::shared_ptr old_master, boost::shared_ptr 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::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 (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 (&items.back ()); + i->set_active (tm->request_mask() & TR_Speed); + items.push_back (CheckMenuElem (_("Accept locate commands"))); + i = dynamic_cast (&items.back ()); + i->set_active (tm->request_mask() & TR_Locate); +} + +void +TransportMastersWidget::Row::connection_handler () +{ +} + +Glib::RefPtr +TransportMastersWidget::Row::build_port_list (vector const & ports) +{ + Glib::RefPtr 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::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 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 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 ttm; + boost::shared_ptr 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 (tm))) { + format.set_text (timecode_format_name (ttm->apparent_timecode_format())); + } else if ((mtm = boost::dynamic_pointer_cast (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::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 index 0000000000..838e021a5a --- /dev/null +++ b/gtk2_ardour/transport_masters_dialog.h @@ -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 +#include + +#include +#include +#include +#include +#include + +#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 tm; + + void update (ARDOUR::Session*, ARDOUR::samplepos_t); + + Row (); + + struct PortColumns : public Gtk::TreeModel::ColumnRecord { + PortColumns() { + add (short_name); + add (full_name); + } + Gtk::TreeModelColumn short_name; + Gtk::TreeModelColumn full_name; + }; + + PortColumns port_columns; + + void populate_port_combo (); + Glib::RefPtr build_port_list (std::vector 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 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 old_master, boost::shared_ptr new_master); + +}; + +class TransportMastersDialog : public ArdourDialog +{ + public: + TransportMastersDialog (); + + void set_session (ARDOUR::Session*); + + private: + TransportMastersWidget w; +}; + + +#endif /* __ardour_gtk_transport_masters_dialog_h__ */ diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index ad61135d32..aadf185b2a 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -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', -- cgit v1.2.3