From 2ee7959dad7ab4cda3a137bcf13c408f8a8dac12 Mon Sep 17 00:00:00 2001 From: Grygorii Zharun Date: Fri, 6 Jun 2014 02:44:52 -0500 Subject: [PATCH] [Summary] Implemeted persistant engine state saving [git-p4: depot-paths = "//Abdaw/dev_main/tracks/": change = 465751] --- gtk2_ardour/ardour_ui.cc | 3 + gtk2_ardour/ardour_ui_ed.cc | 2 +- libs/ardour/ardour/engine_state_controller.h | 26 +- libs/ardour/engine_state_controller.cc | 279 ++++++++++++++++++- 4 files changed, 288 insertions(+), 22 deletions(-) diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 025762f0e8..a9c23a61c0 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -588,6 +588,9 @@ ARDOUR_UI::update_ouput_operation_mode_buttons() ARDOUR_UI::~ARDOUR_UI () { + Config->add_extra_xml(EngineStateController::instance()->serialize_audio_midi_settings() ); + Config->save_state(); + if (ui_config->dirty()) { ui_config->save_state(); } diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index 72c44df3ac..ca01593cb5 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -62,6 +62,7 @@ #include "ardour/session.h" #include "ardour/profile.h" #include "ardour/audioengine.h" +#include "ardour/engine_state_controller.h" #include "control_protocol/control_protocol.h" @@ -564,7 +565,6 @@ ARDOUR_UI::save_ardour_state () window_node->add_child_nocopy (*tearoff_node); Config->add_extra_xml (*window_node); - //VKPRefs:Config->add_extra_xml (audio_midi_setup->get_state()); Config->save_state(); diff --git a/libs/ardour/ardour/engine_state_controller.h b/libs/ardour/ardour/engine_state_controller.h index b604a1e200..ca86467ade 100644 --- a/libs/ardour/ardour/engine_state_controller.h +++ b/libs/ardour/ardour/engine_state_controller.h @@ -119,6 +119,7 @@ public: // switch backend to session sample rate void set_desired_sample_rate(framecnt_t); + XMLNode& serialize_audio_midi_settings(); //DATE UPDATE SIGNALS /* this signal is emitted if the sample rate changes */ @@ -162,21 +163,21 @@ private: std::string device_name; ARDOUR::framecnt_t sample_rate; ARDOUR::pframes_t buffer_size; - uint32_t input_latency; - uint32_t output_latency; + //uint32_t input_latency; not used so far + //uint32_t output_latency; not used so far PortStateList input_channel_states; PortStateList multi_out_channel_states; PortStateList stereo_out_channel_states; - //bool active; - std::string midi_option; + bool active; + //std::string midi_option; not used so far State() - : input_latency (0) - , output_latency (0) + : sample_rate(0) + , buffer_size(0) , input_channel_states (0) , multi_out_channel_states (0) , stereo_out_channel_states (0) - //, active (false) + , active (false) { } @@ -185,6 +186,10 @@ private: return (backend_name == rhs.backend_name) && (device_name == rhs.device_name); } + std::string form_state_name() { + return std::string("State:" + backend_name + ":" + device_name); + } + // predicates for search struct StatePredicate { @@ -232,11 +237,12 @@ private: // state control methods//////////////// void _deserialize_and_load_engine_states(); void _deserialize_and_load_midi_port_states() {}; - void _serialize_and_save_current_engine_state(); - void _serialize_and_save_midi_port_states() {}; + void _serialize_engine_states(XMLNode*); + void _serialize_midi_port_states(XMLNode*) {}; // sets last active state as current state // if no last active state found it loads default state - void _set_last_active_state_as_current(); + void _do_initial_engine_setup(); + bool _apply_state(const StatePtr& state); // get gets available device channels from engine and updates internal controller state void _update_device_channels_state(bool reconnect_session_routes = true); // check stereo out channel state configuration and make it correcpond stereo out mode requirements diff --git a/libs/ardour/engine_state_controller.cc b/libs/ardour/engine_state_controller.cc index d333345e68..9144851126 100644 --- a/libs/ardour/engine_state_controller.cc +++ b/libs/ardour/engine_state_controller.cc @@ -66,7 +66,7 @@ EngineStateController::EngineStateController() Config->ParameterChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_parameter_changed, this, _1) ); _deserialize_and_load_engine_states(); - _set_last_active_state_as_current(); + _do_initial_engine_setup(); _deserialize_and_load_midi_port_states(); // now push the sate to the backend @@ -76,32 +76,283 @@ EngineStateController::EngineStateController() EngineStateController::~EngineStateController() { +} + + +XMLNode& +EngineStateController::serialize_audio_midi_settings() { + XMLNode* root = new XMLNode ("AudioMidiSettings"); + + _serialize_engine_states(root); + _serialize_midi_port_states(root); + + return *root; } void EngineStateController::_deserialize_and_load_engine_states() { + XMLNode* audio_midi_settings_root = ARDOUR::Config->extra_xml ("AudioMidiSettings"); + if (!audio_midi_settings_root) { + return; + } + + XMLNode* engine_states = audio_midi_settings_root->child("EngineStates"); + + if (!engine_states) { + return; + } + + XMLNodeList state_nodes_list = engine_states->children(); + XMLNodeConstIterator state_node_iter = state_nodes_list.begin(); + + for (; state_node_iter != state_nodes_list.end(); ++state_node_iter) { + + XMLNode* state_node = *state_node_iter; + StatePtr engine_state(new State); + XMLProperty* prop = NULL; + + if ((prop = state_node->property ("backend-name")) == 0) { + continue; + } + engine_state->backend_name = prop->value (); + + if ((prop = state_node->property ("device-name")) == 0) { + continue; + } + engine_state->device_name = prop->value (); + + if ((prop = state_node->property ("sample-rate")) == 0) { + continue; + } + engine_state->sample_rate = atoi (prop->value ()); + + if ((prop = state_node->property ("buffer-size")) == 0) { + continue; + } + engine_state->buffer_size = atoi (prop->value ()); + + if ((prop = state_node->property ("active")) == 0) { + continue; + } + engine_state->active = string_is_affirmative (prop->value ()); + + XMLNodeList state_children_list = state_node->children(); + XMLNodeConstIterator state_child_iter = state_children_list.begin(); + + for (; state_child_iter != state_children_list.end(); ++state_child_iter) { + XMLNode* state_child = *state_child_iter; + + if (state_child->name() == "InputConfiguration") { + + XMLNodeList input_states_nodes = state_child->children(); + XMLNodeConstIterator input_state_node_iter = input_states_nodes.begin(); + PortStateList& input_states = engine_state->input_channel_states; + + for (; input_state_node_iter != input_states_nodes.end(); ++input_state_node_iter) { + + XMLNode* input_state_node = *input_state_node_iter; + + if (input_state_node->name() != "input") { + continue; + } + PortState input_state (input_state_node->name() ); + + if ((prop = input_state_node->property ("name")) == 0) { + continue; + } + input_state.name = prop->value(); + + + if ((prop = input_state_node->property ("active")) == 0) { + continue; + } + input_state.active = string_is_affirmative (prop->value ()); + + input_states.push_back(input_state); + } + + } else if (state_child->name() == "MultiOutConfiguration") { + + XMLNodeList multi_out_state_nodes = state_child->children(); + XMLNodeConstIterator multi_out_state_node_iter = multi_out_state_nodes.begin(); + PortStateList& multi_out_states = engine_state->multi_out_channel_states; + + for (; multi_out_state_node_iter != multi_out_state_nodes.end(); ++multi_out_state_node_iter) { + + XMLNode* multi_out_state_node = *multi_out_state_node_iter; + + if (multi_out_state_node->name() != "output") { + continue; + } + PortState multi_out_state (multi_out_state_node->name() ); + + if ((prop = multi_out_state_node->property ("name")) == 0) { + continue; + } + multi_out_state.name = prop->value(); + + if ((prop = multi_out_state_node->property ("active")) == 0) { + continue; + } + multi_out_state.active = string_is_affirmative (prop->value ()); + + multi_out_states.push_back(multi_out_state); + } + } else if (state_child->name() == "StereoOutConfiguration") { + + XMLNodeList stereo_out_state_nodes = state_child->children(); + XMLNodeConstIterator stereo_out_state_node_iter = stereo_out_state_nodes.begin(); + PortStateList& stereo_out_states = engine_state->stereo_out_channel_states; + + for (; stereo_out_state_node_iter != stereo_out_state_nodes.end(); ++stereo_out_state_node_iter) { + + XMLNode* stereo_out_state_node = *stereo_out_state_node_iter; + + if (stereo_out_state_node->name() != "output") { + continue; + } + PortState stereo_out_state (stereo_out_state_node->name() ); + + if ((prop = stereo_out_state_node->property ("name")) == 0) { + continue; + } + stereo_out_state.name = prop->value(); + + if ((prop = stereo_out_state_node->property ("active")) == 0) { + continue; + } + stereo_out_state.active = string_is_affirmative (prop->value ()); + + stereo_out_states.push_back(stereo_out_state); + } + } + } + + _states.push_back (engine_state); + } } void -EngineStateController::_serialize_and_save_current_engine_state() +EngineStateController::_serialize_engine_states(XMLNode* audio_midi_settings_node) { - // **** add code to save the state list to the file **** + if (!audio_midi_settings_node) { + return; + } + + // clean up state data first + audio_midi_settings_node->remove_nodes_and_delete("EngineStates" ); + + XMLNode* engine_states = new XMLNode("EngineStates" ); + + if (!engine_states) { + return; + } + + StateList::const_iterator state_iter = _states.begin(); + for (; state_iter != _states.end(); ++state_iter) { + + StatePtr state_ptr = *state_iter; + + // create new node for the state + XMLNode* state_node = new XMLNode("State"); + + state_node->add_property ("backend-name", state_ptr->backend_name); + state_node->add_property ("device-name", state_ptr->device_name); + state_node->add_property ("sample-rate", state_ptr->sample_rate); + state_node->add_property ("buffer-size", state_ptr->buffer_size); + state_node->add_property ("active", state_ptr->active ? "yes" : "no"); + + // store channel states: + // inputs + XMLNode* input_config_node = new XMLNode("InputConfiguration"); + PortStateList& input_channels = state_ptr->input_channel_states; + PortStateList::const_iterator input_state_iter = input_channels.begin(); + for (; input_state_iter != input_channels.end(); ++input_state_iter) { + XMLNode* input_state_node = new XMLNode("input"); + input_state_node->add_property ("name", input_state_iter->name); + input_state_node->add_property ("active", input_state_iter->active ? "yes" : "no"); + input_config_node->add_child_nocopy(*input_state_node); + } + state_node->add_child_nocopy(*input_config_node); + + // multi out outputs + XMLNode* multi_out_config_node = new XMLNode("MultiOutConfiguration"); + PortStateList& multi_out_channels = state_ptr->multi_out_channel_states; + PortStateList::const_iterator multi_out_state_iter = multi_out_channels.begin(); + for (; multi_out_state_iter != multi_out_channels.end(); ++multi_out_state_iter) { + XMLNode* multi_out_state_node = new XMLNode("output" ); + multi_out_state_node->add_property ("name", multi_out_state_iter->name); + multi_out_state_node->add_property ("active", multi_out_state_iter->active ? "yes" : "no"); + multi_out_config_node->add_child_nocopy(*multi_out_state_node); + } + state_node->add_child_nocopy(*multi_out_config_node); + + // stereo out outputs + XMLNode* stereo_out_config_node = new XMLNode("StereoOutConfiguration"); + PortStateList& stereo_out_channels = state_ptr->stereo_out_channel_states; + PortStateList::const_iterator stereo_out_state_iter = stereo_out_channels.begin(); + for (; stereo_out_state_iter != stereo_out_channels.end(); ++stereo_out_state_iter) { + XMLNode* stereo_out_state_node = new XMLNode("output" ); + stereo_out_state_node->add_property ("name", stereo_out_state_iter->name); + stereo_out_state_node->add_property ("active", stereo_out_state_iter->active ? "yes" : "no"); + stereo_out_config_node->add_child_nocopy(*stereo_out_state_node); + } + state_node->add_child_nocopy(*stereo_out_config_node); + + engine_states->add_child_nocopy(*state_node); + } + + audio_midi_settings_node->add_child_nocopy(*engine_states); +} + + +bool +EngineStateController::_apply_state(const StatePtr& state) +{ + bool applied = false; + + if (set_new_backend_as_current(state->backend_name) ) { + applied = set_new_device_as_current(state->device_name); + } + + return applied; } void -EngineStateController::_set_last_active_state_as_current() +EngineStateController::_do_initial_engine_setup() { + bool state_applied = false; + // if we have no saved state load default values if (!_states.empty() ) { - } else { - _current_state = boost::shared_ptr(new State() ); + // look for last active state first + StateList::const_iterator state_iter = _states.begin(); + for (; state_iter != _states.end(); ++state_iter) { + if ( (*state_iter)->active ) { + state_applied = _apply_state(*state_iter); + break; + } + } + + // last active state was not applied + // try others + if (!state_applied) { + StateList::const_iterator state_iter = _states.begin(); + for (; state_iter != _states.end(); ++state_iter) { + state_applied = _apply_state(*state_iter); + break; + } + } + } + + if (!state_applied ){ std::vector backends = AudioEngine::instance()->available_backends(); if (!backends.empty() ) { @@ -257,11 +508,16 @@ EngineStateController::set_new_backend_as_current(const std::string& backend_nam boost::shared_ptr backend = AudioEngine::instance()->set_backend (backend_name, "ardour", ""); if (backend) { + if (_current_state != NULL) { + _current_state->active = false; + } + StateList::iterator found_state_iter = find_if (_states.begin(), _states.end(), State::StatePredicate(backend_name, "None") ); if (found_state_iter != _states.end() ) { // we found a record for new engine with None device - switch to it + _current_state = *found_state_iter; } else { @@ -273,6 +529,7 @@ EngineStateController::set_new_backend_as_current(const std::string& backend_nam _states.push_front(_current_state); } + push_current_state_to_backend(false); return true; } @@ -306,6 +563,10 @@ EngineStateController::set_new_device_as_current(const std::string& device_name) // device is available if (device_iter != device_vector.end() ) { + if (_current_state != NULL) { + _current_state->active = false; + } + // look through state list and find the record for this device and current engine StateList::iterator found_state_iter = find_if (_states.begin(), _states.end(), State::StatePredicate(backend->name(), device_name) ); @@ -635,7 +896,6 @@ EngineStateController::set_physical_midi_input_state(const std::string& port_nam // *************************** // add actions here - _serialize_and_save_midi_port_states(); MIDIInputConfigChanged(); } } @@ -653,7 +913,6 @@ EngineStateController::set_physical_midi_output_state(const std::string& port_na // *************************** // add actions here - _serialize_and_save_midi_port_states(); MIDIOutputConfigChanged(); } } @@ -1010,8 +1269,6 @@ EngineStateController::_update_device_channels_state(bool reconnect_session_rout } } - _serialize_and_save_midi_port_states(); - if (reconnect_session_routes) { AudioEngine::instance()->reconnect_session_routes(); @@ -1059,7 +1316,7 @@ EngineStateController::_refresh_stereo_out_channel_states() void EngineStateController::_on_engine_running () { - _serialize_and_save_current_engine_state(); + _current_state->active = true; EngineRunning(); // emit a signal }