diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index 3e64e4f125..6ea38648b0 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -285,6 +285,76 @@ private: }; +/** Component which provides the UI to handle an enumerated option using a GTK ComboBox. + * The template parameter is the enumeration. + */ +class ComboStringOption : public Option +{ +public: + + /** Construct an ComboOption. + * @param i id + * @param n User-visible name. + * @param g Slot to get the variable's value. + * @param s Slot to set the variable's value. + */ + ComboStringOption ( + std::string const & i, + std::string const & n, + sigc::slot g, + sigc::slot s + ) + : Option (i, n), + _get (g), + _set (s) + { + _label = manage (new Gtk::Label (n + ":")); + _label->set_alignment (0, 0.5); + _combo = manage (new Gtk::ComboBoxText); + _combo->signal_changed().connect (sigc::mem_fun (*this, &ComboStringOption::changed)); + } + + void set_state_from_config () { + _combo->set_active_text (_get()); + } + + void add_to_page (OptionEditorPage* p) + { + add_widgets_to_page (p, _label, _combo); + } + + /** Set the allowed strings for this option + * @param strings a vector of allowed strings + */ + void set_popdown_strings (const std::vector& strings) { + _combo->clear_items (); + for (std::vector::const_iterator i = strings.begin(); i != strings.end(); ++i) { + _combo->append_text (*i); + } + } + + void clear () { + _combo->clear_items(); + } + + void changed () { + _set (_combo->get_active_text ()); + } + + void set_sensitive (bool yn) { + _combo->set_sensitive (yn); + } + + Gtk::Widget& tip_widget() { return *_combo; } + +private: + sigc::slot _get; + sigc::slot _set; + Gtk::Label* _label; + Gtk::ComboBoxText* _combo; +}; + + /** Component which provides the UI to handle a boolean option which needs * to be represented as a ComboBox to be clear to the user. */ diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index 6b59e8718e..c889962158 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -1068,8 +1068,23 @@ RCOptionEditor::RCOptionEditor () (_sync_genlock->tip_widget(), _("When enabled indicates that the selected external timecode source shares sync (Black & Burst, Wordclock, etc) with the audio interface")); + add_option (_("Transport"), _sync_genlock); + _ltc_port = new ComboStringOption ( + "ltc-source-port", + _("LTC incoming port"), + sigc::mem_fun (*_rc_config, &RCConfiguration::get_ltc_source_port), + sigc::mem_fun (*_rc_config, &RCConfiguration::set_ltc_source_port) + ); + + vector physical_inputs; + physical_inputs.push_back (_("None")); + AudioEngine::instance()->get_physical_inputs (DataType::AUDIO, physical_inputs); + _ltc_port->set_popdown_strings (physical_inputs); + + add_option (_("Transport"), _ltc_port); + parameter_changed ("sync-source"); /* EDITOR */ diff --git a/gtk2_ardour/rc_option_editor.h b/gtk2_ardour/rc_option_editor.h index eae27d323b..76978d4317 100644 --- a/gtk2_ardour/rc_option_editor.h +++ b/gtk2_ardour/rc_option_editor.h @@ -45,6 +45,7 @@ private: ComboOption* _sync_source; BoolOption* _sync_framerate; BoolOption* _sync_genlock; + ComboStringOption* _ltc_port; PBD::ScopedConnection parameter_change_connection; }; diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 4e55ea2140..49e3c15457 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -260,9 +260,7 @@ _ the regular process() call to session->process() is not made. int create_process_thread (boost::function, pthread_t*, size_t stacksize); -#ifdef HAVE_LTC - Port *ltc_input_port() const { return _ltc_input; } -#endif + boost::shared_ptr ltc_input_port() const { return _ltc_input; } private: static AudioEngine* _instance; @@ -293,9 +291,8 @@ private: Glib::Threads::Thread* m_meter_thread; ProcessThread* _main_thread; -#ifdef HAVE_LTC - Port* _ltc_input; -#endif + boost::shared_ptr _ltc_input; + void reconnect_ltc (); SerializedRCUManager ports; @@ -329,6 +326,7 @@ private: int jack_bufsize_callback (pframes_t); int jack_sample_rate_callback (pframes_t); void freewheel_callback (int); + void connect_callback (jack_port_id_t, jack_port_id_t, int); void set_jack_callbacks (); @@ -356,6 +354,8 @@ private: }; static void* _start_process_thread (void*); + void parameter_changed (const std::string&); + PBD::ScopedConnection config_connection; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index adcc00ec87..9432b7230f 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -51,6 +51,7 @@ CONFIG_VARIABLE (int, mtc_qf_speed_tolerance, "mtc-qf-speed-tolerance", 5) CONFIG_VARIABLE (bool, timecode_sync_frame_rate, "timecode-sync-frame-rate", true) CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true) CONFIG_VARIABLE (SyncSource, sync_source, "sync-source", JACK) +CONFIG_VARIABLE (std::string, ltc_source_port, "ltc-source-port", "system:capture_1") /* control surfaces */ diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 9fb1d1d706..ba7666f3e4 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -89,13 +89,24 @@ AudioEngine::AudioEngine (string client_name, string session_uuid) } Port::set_engine (this); + #ifdef HAVE_LTC - _ltc_input = new AudioPort ("LTC in", Port::IsInput); + _ltc_input = register_port (DataType::AUDIO, _("LTC in"), Port::IsInput); + + /* As of October 2012, the LTC source port is the only thing that needs + * to care about Config parameters, so don't bother to listen if we're + * not doing LTC stuff. This might change if other parameters show up + * in the future that we need to care about with or without LTC. + */ + + Config->ParameterChanged.connect_same_thread (config_connection, boost::bind (&AudioEngine::parameter_changed, this, _1)); #endif } AudioEngine::~AudioEngine () { + config_connection.disconnect (); + { Glib::Threads::Mutex::Lock tm (_process_lock); session_removed.signal (); @@ -210,6 +221,9 @@ AudioEngine::start () _running = true; _has_run = true; Running(); /* EMIT SIGNAL */ + + reconnect_ltc (); + } else { // error << _("cannot activate JACK client") << endmsg; } @@ -377,31 +391,38 @@ void AudioEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg) { AudioEngine* ae = static_cast (arg); + ae->connect_callback (id_a, id_b, conn); +} - if (ae->port_remove_in_progress) { +void +AudioEngine::connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn) +{ + if (port_remove_in_progress) { return; } - GET_PRIVATE_JACK_POINTER (ae->_jack); + GET_PRIVATE_JACK_POINTER (_jack); jack_port_t* jack_port_a = jack_port_by_id (_priv_jack, id_a); jack_port_t* jack_port_b = jack_port_by_id (_priv_jack, id_b); boost::shared_ptr port_a; boost::shared_ptr port_b; + Ports::iterator x; + boost::shared_ptr pr = ports.reader (); - boost::shared_ptr pr = ae->ports.reader (); - Ports::iterator i = pr->begin (); - while (i != pr->end() && (port_a == 0 || port_b == 0)) { - if (jack_port_a == i->second->jack_port()) { - port_a = i->second; - } else if (jack_port_b == i->second->jack_port()) { - port_b = i->second; - } - ++i; + + x = pr->find (make_port_name_relative (jack_port_name (jack_port_a))); + if (x != pr->end()) { + port_a = x->second; } - ae->PortConnectedOrDisconnected ( + x = pr->find (make_port_name_relative (jack_port_name (jack_port_b))); + if (x != pr->end()) { + port_b = x->second; + } + + PortConnectedOrDisconnected ( port_a, jack_port_name (jack_port_a), port_b, jack_port_name (jack_port_b), conn == 0 ? false : true @@ -1470,6 +1491,8 @@ AudioEngine::reconnect_to_jack () MIDI::Manager::instance()->reconnect (); + reconnect_ltc (); + Running (); /* EMIT SIGNAL*/ start_metering_thread (); @@ -1613,3 +1636,27 @@ AudioEngine::destroy () delete _instance; _instance = 0; } + +void +AudioEngine::parameter_changed (const std::string& s) +{ + if (s == "ltc-source-port") { + reconnect_ltc (); + } + +} + +void +AudioEngine::reconnect_ltc () +{ + if (_ltc_input) { + + string src = Config->get_ltc_source_port(); + + _ltc_input->disconnect_all (); + + if (src != _("None") && !src.empty()) { + _ltc_input->connect (src); + } + } +} diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc index 292426ed04..52c49737e0 100644 --- a/libs/ardour/ltc_slave.cc +++ b/libs/ardour/ltc_slave.cc @@ -307,7 +307,7 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos) jack_default_audio_sample_t *in; jack_latency_range_t ltc_latency; - Port *ltcport = session.engine().ltc_input_port(); + boost::shared_ptr ltcport = session.engine().ltc_input_port(); ltcport->get_connected_latency_range(ltc_latency, false); in = (jack_default_audio_sample_t*) jack_port_get_buffer (ltcport->jack_port(), nframes);