From ab51cc7f7223bd0506f0743c735e3ddf16cedd97 Mon Sep 17 00:00:00 2001 From: GZharun Date: Thu, 13 Nov 2014 17:13:38 +0200 Subject: [PATCH] [Summary] Implemented MTC selection mechanism with state saving/restoring. Implemented MTC use cases: Switch to internal time code when MTC input is switched to off, disabling MTC timecode source when not MTC input is set. Fixed Bug when it was impossible to switch to internal timecode source when transport was started with MTC. --- gtk2_ardour/ardour_ui.h | 1 + gtk2_ardour/ardour_ui_dialogs.cc | 2 + gtk2_ardour/ardour_ui_ed.cc | 22 +++++- gtk2_ardour/ardour_ui_options.cc | 1 + gtk2_ardour/tracks_control_panel.cc | 1 + gtk2_ardour/tracks_control_panel.h | 1 + gtk2_ardour/tracks_control_panel.logic.cc | 83 ++++++++++++++++++++ gtk2_ardour/tracks_control_panel.logic.h | 3 + gtk2_ardour/waves_dropdown.cc | 23 ++++++ gtk2_ardour/waves_dropdown.h | 1 + libs/ardour/ardour/engine_state_controller.h | 10 ++- libs/ardour/ardour/session.h | 4 + libs/ardour/engine_state_controller.cc | 46 +++++++++-- libs/ardour/session.cc | 25 ++++++ 14 files changed, 216 insertions(+), 7 deletions(-) diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 82b4bb8654..e987cfbe4e 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -252,6 +252,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr WavesDropdown* _timecode_source_dropdown; void on_timecode_source_dropdown_item_clicked (WavesDropdown*, int); void populate_timecode_source_dropdown (); + void update_timecode_source_dropdown_items (); void update_bit_depth_button (); void update_sample_rate_dropdown (); diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index a5fe4afbc2..b4eb2b60e4 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -163,6 +163,8 @@ ARDOUR_UI::set_session (Session *s) _session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context()); _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ()); + _session->MTCInputPortChanged.connect(_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_timecode_source_dropdown_items, this), gui_context ()); + /* Clocks are on by default after we are connected to a session, so show that here. */ diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index 06a3c05fea..88030dfd72 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -182,6 +182,26 @@ ARDOUR_UI::populate_timecode_source_dropdown () } else { _timecode_source_dropdown->set_text (timecode_source[0]);//Internal } + + update_timecode_source_dropdown_items(); +} + +void +ARDOUR_UI::update_timecode_source_dropdown_items() +{ + if (!_session) { + return; + } + + Gtk::MenuItem* mtc_item = _timecode_source_dropdown->get_item ("MTC"); + + if (mtc_item) { + if (_session->mtc_input_port()->connected() ) { + mtc_item->set_sensitive(true); + } else { + mtc_item->set_sensitive(false); + } + } } void @@ -215,7 +235,7 @@ ARDOUR_UI::on_timecode_source_dropdown_item_clicked (WavesDropdown* dropdown, in string timecode_source = *(string*)data; - if ( timecode_source == "Intermal" ) + if ( timecode_source == "Internal" ) { _session->config.set_external_sync (false); } else if ( timecode_source == "MTC" ) diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index de19a8d5af..c934b0b6f3 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -326,6 +326,7 @@ ARDOUR_UI::parameter_changed (std::string p) ActionManager::get_action ("Transport", "ToggleFollowEdits")->set_sensitive (false); } + populate_timecode_source_dropdown (); } else if (p == "always-play-range") { ActionManager::map_some_state ("Transport", "ToggleFollowEdits", &RCConfiguration::get_follow_edits); diff --git a/gtk2_ardour/tracks_control_panel.cc b/gtk2_ardour/tracks_control_panel.cc index 967cf9181b..b4b9db5c13 100644 --- a/gtk2_ardour/tracks_control_panel.cc +++ b/gtk2_ardour/tracks_control_panel.cc @@ -52,6 +52,7 @@ TracksControlPanel::TracksControlPanel () , _device_dropdown (get_waves_dropdown ("device_dropdown")) , _sample_rate_dropdown (get_waves_dropdown ("sample_rate_dropdown")) , _buffer_size_dropdown (get_waves_dropdown ("buffer_size_dropdown")) + , _mtc_in_dropdown (get_waves_dropdown ("mtc_in")) , _latency_label (get_label("latency_label")) , _default_open_path (get_label("default_open_path")) , _multi_out_button(get_waves_button ("multi_out_button")) diff --git a/gtk2_ardour/tracks_control_panel.h b/gtk2_ardour/tracks_control_panel.h index 3f1c5c84cd..4ac853cd23 100644 --- a/gtk2_ardour/tracks_control_panel.h +++ b/gtk2_ardour/tracks_control_panel.h @@ -84,6 +84,7 @@ class TracksControlPanel : public WavesDialog, public PBD::ScopedConnectionList WavesDropdown& _device_dropdown; WavesDropdown& _sample_rate_dropdown; WavesDropdown& _buffer_size_dropdown; + WavesDropdown& _mtc_in_dropdown; WavesDropdown& _file_type_dropdown; WavesDropdown& _bit_depth_dropdown; WavesDropdown& _frame_rate_dropdown; diff --git a/gtk2_ardour/tracks_control_panel.logic.cc b/gtk2_ardour/tracks_control_panel.logic.cc index 025ca28031..add2c64fc8 100644 --- a/gtk2_ardour/tracks_control_panel.logic.cc +++ b/gtk2_ardour/tracks_control_panel.logic.cc @@ -120,6 +120,7 @@ TracksControlPanel::init () EngineStateController::instance()->OutputConfigChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_audio_output_configuration_changed, this), gui_context()); EngineStateController::instance()->MIDIInputConfigChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_midi_input_configuration_changed, this), gui_context()); EngineStateController::instance()->MIDIOutputConfigChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_midi_output_configuration_changed, this), gui_context()); + EngineStateController::instance()->MTCInputChanged.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_mtc_input_changed, this, _1), gui_context()); EngineStateController::instance()->DeviceError.connect (update_connections, MISSING_INVALIDATOR, boost::bind (&TracksControlPanel::on_device_error, this), gui_context()); /* Global configuration parameters update */ @@ -129,6 +130,7 @@ TracksControlPanel::init () _device_dropdown.selected_item_changed.connect (sigc::mem_fun(*this, &TracksControlPanel::on_device_dropdown_item_clicked)); _sample_rate_dropdown.selected_item_changed.connect (sigc::mem_fun(*this, &TracksControlPanel::on_sample_rate_dropdown_item_clicked)); _buffer_size_dropdown.selected_item_changed.connect (sigc::mem_fun(*this, &TracksControlPanel::on_buffer_size_dropdown_item_clicked)); + _mtc_in_dropdown.selected_item_changed.connect (sigc::mem_fun(*this, &TracksControlPanel::on_mtc_input_chosen)); /* Session configuration parameters update */ _file_type_dropdown.selected_item_changed.connect (sigc::mem_fun(*this, &TracksControlPanel::on_file_type_dropdown_item_clicked)); @@ -148,6 +150,8 @@ TracksControlPanel::init () populate_engine_dropdown (); populate_device_dropdown (); + populate_mtc_in_dropdown (); + populate_output_mode (); populate_file_type_dropdown (); @@ -689,6 +693,44 @@ TracksControlPanel::populate_buffer_size_dropdown() } } +void +TracksControlPanel::populate_mtc_in_dropdown() +{ + std::vector midi_states; + static const char* midi_port_name_prefix = "system_midi:"; + const char* midi_type_suffix; + bool have_first = false; + + EngineStateController::instance()->get_physical_midi_input_states(midi_states); + midi_type_suffix = X_(" capture"); + + _mtc_in_dropdown.clear_items (); + + Gtk::MenuItem& off_item = _mtc_in_dropdown.add_menu_item ("Off", 0); + + std::vector::const_iterator state_iter; + for (state_iter = midi_states.begin(); state_iter != midi_states.end(); ++state_iter) { + + // strip the device name from input port name + std::string device_name; + ARDOUR::remove_pattern_from_string(state_iter->name, midi_port_name_prefix, device_name); + ARDOUR::remove_pattern_from_string(device_name, midi_type_suffix, device_name); + + if (state_iter->active) { + Gtk::MenuItem& new_item = _mtc_in_dropdown.add_menu_item (device_name, strdup(state_iter->name.c_str()) ); + + if (!have_first && state_iter->mtc_in) { + _mtc_in_dropdown.set_text (new_item.get_label() ); + have_first = true; + } + } + } + + if (!have_first) { + _mtc_in_dropdown.set_text (off_item.get_label() ); + } +} + void TracksControlPanel::populate_output_mode() { @@ -1310,6 +1352,20 @@ TracksControlPanel::on_sample_rate_dropdown_item_clicked (WavesDropdown*, int) } +void +TracksControlPanel::on_mtc_input_chosen (WavesDropdown* dropdown, int el_number) +{ + char* full_name_of_chosen_port = (char*)dropdown->get_item_associated_data(el_number); + + if (full_name_of_chosen_port) { + EngineStateController::instance()->set_mtc_input((char*) full_name_of_chosen_port); + } else { + EngineStateController::instance()->set_mtc_input(""); + } + +} + + void TracksControlPanel::engine_running () { @@ -1553,6 +1609,7 @@ void TracksControlPanel::on_port_registration_update() populate_input_channels(); populate_output_channels(); populate_midi_ports(); + populate_mtc_in_dropdown(); } @@ -1712,6 +1769,13 @@ TracksControlPanel::on_midi_input_configuration_changed () } } } + + populate_mtc_in_dropdown(); + + ARDOUR::Session* session = ARDOUR_UI::instance()->the_session(); + if (session) { + session->reconnect_mtc_ports (); + } } @@ -1739,6 +1803,25 @@ TracksControlPanel::on_midi_output_configuration_changed () } +void +TracksControlPanel::on_mtc_input_changed (const std::string&) +{ + ARDOUR_UI* ardour_ui = ARDOUR_UI::instance(); + + if(!ardour_ui) + { + return; + } + + ARDOUR::Session* session = ardour_ui->the_session(); + if (!session) { + return; + } + + session->reconnect_mtc_ports (); +} + + std::string TracksControlPanel::bufsize_as_string (uint32_t sz) { diff --git a/gtk2_ardour/tracks_control_panel.logic.h b/gtk2_ardour/tracks_control_panel.logic.h index 4913386292..61a2d8ac30 100644 --- a/gtk2_ardour/tracks_control_panel.logic.h +++ b/gtk2_ardour/tracks_control_panel.logic.h @@ -79,6 +79,7 @@ void buffer_size_changed (); void on_buffer_size_dropdown_item_clicked (WavesDropdown*, int); void on_sample_rate_dropdown_item_clicked (WavesDropdown*, int); + void on_mtc_input_chosen (WavesDropdown*, int); void engine_running (); void engine_stopped (); void on_file_type_dropdown_item_clicked (WavesDropdown*, int); @@ -89,6 +90,7 @@ void populate_device_dropdown (); void populate_sample_rate_dropdown (); void populate_buffer_size_dropdown (); + void populate_mtc_in_dropdown (); void populate_output_mode (); void populate_input_channels(); void populate_output_channels(); @@ -112,6 +114,7 @@ void on_audio_output_configuration_changed (); void on_midi_input_configuration_changed (); void on_midi_output_configuration_changed (); + void on_mtc_input_changed (const std::string&); void on_device_error (); // Merged ARDOUR's preferences diff --git a/gtk2_ardour/waves_dropdown.cc b/gtk2_ardour/waves_dropdown.cc index 818eb68ad0..ac965b8171 100644 --- a/gtk2_ardour/waves_dropdown.cc +++ b/gtk2_ardour/waves_dropdown.cc @@ -43,6 +43,10 @@ void* WavesDropdown::get_item_associated_data(int item_number) { Gtk::Menu_Helpers::MenuList& items = _menu.items (); + if (item_number >= items.size() ) { + return NULL; + } + Gtk::Menu_Helpers::MenuList::iterator i = items.begin(); std::advance (i, item_number); @@ -53,12 +57,31 @@ Gtk::MenuItem* WavesDropdown::get_item (int item_number) { Gtk::Menu_Helpers::MenuList& items = _menu.items (); + if (item_number >= items.size() ) { + return NULL; + } + Gtk::Menu_Helpers::MenuList::iterator i = items.begin(); std::advance (i, item_number); return &(*i); } +Gtk::MenuItem* +WavesDropdown::get_item (const std::string& text_label) +{ + Gtk::Menu_Helpers::MenuList& items = _menu.items (); + Gtk::Menu_Helpers::MenuList::iterator i = items.begin(); + for (; i != items.end(); ++i) { + if (i->get_label() == text_label) { + return &(*i); + } + } + + return NULL; +} + + void WavesDropdown::set_current_item (int current_item_number) { diff --git a/gtk2_ardour/waves_dropdown.h b/gtk2_ardour/waves_dropdown.h index d9784a1733..28f75907c2 100644 --- a/gtk2_ardour/waves_dropdown.h +++ b/gtk2_ardour/waves_dropdown.h @@ -38,6 +38,7 @@ class WavesDropdown : public WavesIconButton void* get_item_associated_data (int); Gtk::MenuItem* get_item (int); + Gtk::MenuItem* get_item (const std::string&); Gtk::MenuItem& add_menu_item (const std::string& item, void* cookie = 0); Gtk::RadioMenuItem& add_radio_menu_item (const std::string& item, void* cookie = 0); diff --git a/libs/ardour/ardour/engine_state_controller.h b/libs/ardour/ardour/engine_state_controller.h index c953f3a676..76a2d88119 100644 --- a/libs/ardour/ardour/engine_state_controller.h +++ b/libs/ardour/ardour/engine_state_controller.h @@ -52,12 +52,14 @@ public: bool active; bool available; bool connected; + bool mtc_in; MidiPortState(const std::string& name): name(name), active(false), available(false), - connected(false) + connected(false), + mtc_in(false) {} bool operator==(const MidiPortState& rhs) @@ -131,6 +133,9 @@ public: void set_all_midi_inputs_disconnected(); void set_all_midi_outputs_disconnected(); + // set the state for MIDI TimeCode connection + void set_mtc_input(const std::string&); + bool is_setup_required() const {return ARDOUR::AudioEngine::instance()->setup_required (); } // set parameters inside the controller, @@ -181,6 +186,9 @@ public: PBD::Signal2&, bool> MIDIInputConnectionChanged; PBD::Signal2&, bool> MIDIOutputConnectionChanged; + /* this signals are emitted if the MTC i/o channel configuration changes */ + PBD::Signal1 MTCInputChanged; + /* this signal is emitted if new ports are registered or unregistered */ PBD::Signal0 PortRegistrationChanged; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index d4688ac70b..9469938ab3 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -341,6 +341,9 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop /* Step Editing status changed */ PBD::Signal1 StepEditStatusChange; + /* MTC state signals */ + PBD::Signal0 MTCInputPortChanged; + void queue_event (SessionEvent*); void request_roll_at_and_return (framepos_t start, framepos_t return_to); @@ -946,6 +949,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop MIDI::MachineControl& mmc() { return *_mmc; } void reconnect_midi_scene_ports (bool); + void reconnect_mtc_ports(); protected: friend class AudioEngine; diff --git a/libs/ardour/engine_state_controller.cc b/libs/ardour/engine_state_controller.cc index 66555baeed..de2c75b638 100644 --- a/libs/ardour/engine_state_controller.cc +++ b/libs/ardour/engine_state_controller.cc @@ -194,7 +194,6 @@ EngineStateController::_deserialize_and_load_engine_states() } input_state.name = prop->value(); - if ((prop = input_state_node->property ("active")) == 0) { continue; } @@ -267,7 +266,7 @@ EngineStateController::_deserialize_and_load_engine_states() void EngineStateController::_deserialize_and_load_midi_port_states() -{ +{ XMLNode* audio_midi_settings_root = ARDOUR::Config->extra_xml ("AudioMidiSettings"); if (!audio_midi_settings_root) { @@ -316,6 +315,11 @@ EngineStateController::_deserialize_and_load_midi_port_states() } input_state.connected = string_is_affirmative (prop->value ()); + if ((prop = input_state_node->property ("mtc-in")) == 0) { + continue; + } + input_state.mtc_in = string_is_affirmative (prop->value ()); + _midi_inputs.push_back(input_state); } @@ -350,6 +354,11 @@ EngineStateController::_deserialize_and_load_midi_port_states() } output_state.connected = string_is_affirmative (prop->value ()); + if ((prop = output_state_node->property ("mtc-in")) == 0) { + continue; + } + output_state.mtc_in = string_is_affirmative (prop->value ()); + _midi_outputs.push_back(output_state); } } @@ -446,6 +455,7 @@ EngineStateController::_serialize_midi_port_states(XMLNode* audio_midi_settings_ midi_input_node->add_property ("name", midi_input_state_iter->name); midi_input_node->add_property ("active", midi_input_state_iter->active ? "yes" : "no"); midi_input_node->add_property ("connected", midi_input_state_iter->connected ? "yes" : "no"); + midi_input_node->add_property ("mtc-in", midi_input_state_iter->mtc_in ? "yes" : "no"); midi_input_states_node->add_child_nocopy(*midi_input_node); } midi_states_node->add_child_nocopy(*midi_input_states_node); @@ -456,7 +466,8 @@ EngineStateController::_serialize_midi_port_states(XMLNode* audio_midi_settings_ XMLNode* midi_output_node = new XMLNode("output" ); midi_output_node->add_property ("name", midi_output_state_iter->name); midi_output_node->add_property ("active", midi_output_state_iter->active ? "yes" : "no"); - midi_output_node->add_property ("connected", midi_input_state_iter->connected ? "yes" : "no"); + midi_output_node->add_property ("connected", midi_output_state_iter->connected ? "yes" : "no"); + midi_output_node->add_property ("mtc-in", midi_output_state_iter->mtc_in ? "yes" : "no"); midi_output_states_node->add_child_nocopy(*midi_output_node); } midi_states_node->add_child_nocopy(*midi_output_states_node); @@ -1167,6 +1178,22 @@ EngineStateController::set_all_midi_outputs_disconnected() } +void +EngineStateController::set_mtc_input(const std::string& port_name) +{ + MidiPortStateList::iterator iter = _midi_inputs.begin(); + for (; iter != _midi_inputs.end(); ++iter) { + iter->mtc_in = false; + + if (iter->name == port_name) { + iter->mtc_in = true; + } + } + + MTCInputChanged(port_name); +} + + void EngineStateController::set_state_to_all_inputs(bool state) { @@ -1245,7 +1272,9 @@ EngineStateController::get_physical_midi_input_states (std::vectoravailable) { MidiPortState state(iter->name); state.active = iter->active; + state.available = true; state.connected = iter->connected; + state.mtc_in = iter->mtc_in; channel_states.push_back(state); } } @@ -1262,7 +1291,9 @@ EngineStateController::get_physical_midi_output_states (std::vectoravailable) { MidiPortState state(iter->name); state.active = iter->active; + state.available = true; state.connected = iter->connected; + state.mtc_in = iter->mtc_in; channel_states.push_back(state); } } @@ -1272,9 +1303,14 @@ EngineStateController::get_physical_midi_output_states (std::vectorreconnect_session_routes(true, true); + if (!_session) { + return; + } - if (_session && _desired_sample_rate && set_new_sample_rate_in_controller(_desired_sample_rate) ) + AudioEngine::instance()->reconnect_session_routes(true, true); + _session->reconnect_mtc_ports (); + + if (_session && _desired_sample_rate && set_new_sample_rate_in_controller(_desired_sample_rate) ) { push_current_state_to_backend(false); SampleRateChanged(); // emit a signal diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 2d7f5d0018..29acd8a87c 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2491,6 +2491,31 @@ Session::reconnect_midi_scene_ports(bool inputs) } +void +Session::reconnect_mtc_ports() +{ + _midi_ports->mtc_input_port()->disconnect_all (); + + std::vector midi_port_states; + EngineStateController::instance()->get_physical_midi_input_states (midi_port_states); + + std::vector::iterator state_iter = midi_port_states.begin(); + + for (; state_iter != midi_port_states.end(); ++state_iter) { + if (state_iter->active && state_iter->available && state_iter->mtc_in) { + _midi_ports->mtc_input_port()->connect (state_iter->name); + } + } + + if (!_midi_ports->mtc_input_port()->connected() && + config.get_external_sync () ) { + config.set_external_sync (false); + } + + MTCInputPortChanged(); //emit signal +} + + /** Caller must not hold process lock * @param name_template string to use for the start of the name, or "" to use "Audio". */