[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.
This commit is contained in:
GZharun 2014-11-13 17:13:38 +02:00
parent 3ee3020611
commit ab51cc7f72
14 changed files with 216 additions and 7 deletions

View file

@ -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 ();

View file

@ -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.
*/

View file

@ -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" )

View file

@ -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);

View file

@ -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"))

View file

@ -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;

View file

@ -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<EngineStateController::MidiPortState> 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<EngineStateController::MidiPortState>::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)
{

View file

@ -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

View file

@ -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)
{

View file

@ -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);

View file

@ -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<void, const std::vector<std::string>&, bool> MIDIInputConnectionChanged;
PBD::Signal2<void, const std::vector<std::string>&, bool> MIDIOutputConnectionChanged;
/* this signals are emitted if the MTC i/o channel configuration changes */
PBD::Signal1<void, const std::string&> MTCInputChanged;
/* this signal is emitted if new ports are registered or unregistered */
PBD::Signal0<void> PortRegistrationChanged;

View file

@ -341,6 +341,9 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
/* Step Editing status changed */
PBD::Signal1<void,bool> StepEditStatusChange;
/* MTC state signals */
PBD::Signal0<void> 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;

View file

@ -194,7 +194,6 @@ EngineStateController::_deserialize_and_load_engine_states()
}
input_state.name = prop->value();
if ((prop = input_state_node->property ("active")) == 0) {
continue;
}
@ -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::vector<MidiPortState
if (iter->available) {
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::vector<MidiPortStat
if (iter->available) {
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::vector<MidiPortStat
void
EngineStateController::_on_session_loaded ()
{
AudioEngine::instance()->reconnect_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

View file

@ -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<EngineStateController::MidiPortState> midi_port_states;
EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
std::vector<EngineStateController::MidiPortState>::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".
*/