/* Copyright (C) 2014 Waves Audio Ltd. 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 "ardour/engine_state_controller.h" #include "ardour/audioengine.h" #include "ardour/session.h" #include "ardour/rc_configuration.h" #include "ardour/data_type.h" #include "pbd/pthread_utils.h" #include "pbd/error.h" #include "i18n.h" using namespace ARDOUR; using namespace PBD; namespace { struct DevicePredicate { DevicePredicate(const std::string& device_name) : _device_name(device_name) {} bool operator()(const AudioBackend::DeviceStatus& rhs) { return _device_name == rhs.name; } private: std::string _device_name; }; } EngineStateController* EngineStateController::instance() { static EngineStateController instance; return &instance; } EngineStateController::EngineStateController() : _current_state() , _last_used_real_device("") { AudioEngine::instance ()->Running.connect_same_thread (running_connection, boost::bind (&EngineStateController::_on_engine_running, this) ); AudioEngine::instance ()->Stopped.connect_same_thread (stopped_connection, boost::bind (&EngineStateController::_on_engine_stopped, this) ); AudioEngine::instance ()->Halted.connect_same_thread (stopped_connection, boost::bind (&EngineStateController::_on_engine_stopped, this) ); /* Subscribe for udpates from AudioEngine */ AudioEngine::instance ()->PortRegisteredOrUnregistered.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_ports_registration_update, this) ); AudioEngine::instance()->SampleRateChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_sample_rate_change, this, _1) ); AudioEngine::instance()->BufferSizeChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_buffer_size_change, this, _1) ); AudioEngine::instance()->DeviceListChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_device_list_change, this) ); AudioEngine::instance()->DeviceError.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_device_error, this) ); /* Global configuration parameters update */ Config->ParameterChanged.connect_same_thread (update_connections, boost::bind (&EngineStateController::_on_parameter_changed, this, _1) ); _deserialize_and_load_engine_states(); _deserialize_and_load_midi_port_states(); _do_initial_engine_setup(); // now push the sate to the backend push_current_state_to_backend(false); } EngineStateController::~EngineStateController() { } void EngineStateController::set_session(Session* session) { _session = session; _session->SessionLoaded.connect_same_thread (session_connections, boost::bind (&EngineStateController::_on_session_loaded, this) ); } void EngineStateController::remove_session () { session_connections.drop_connections (); _session = 0; } XMLNode& EngineStateController::serialize_audio_midi_settings() { XMLNode* root = new XMLNode ("AudioMidiSettings"); _serialize_engine_states(root); _serialize_midi_port_states(root); return *root; } void EngineStateController::save_audio_midi_settings() { Config->add_extra_xml (serialize_audio_midi_settings() ); Config->save_state (); } 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::_deserialize_and_load_midi_port_states() { XMLNode* audio_midi_settings_root = ARDOUR::Config->extra_xml ("AudioMidiSettings"); if (!audio_midi_settings_root) { return; } XMLNode* midi_states = audio_midi_settings_root->child("MidiStates"); if (!midi_states) { return; } XMLNodeList state_nodes_list = midi_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; if (state_node->name() == "MidiInputs") { XMLNodeList input_state_nodes = state_node->children(); XMLNodeConstIterator input_state_node_iter = input_state_nodes.begin(); _midi_inputs.clear(); for (; input_state_node_iter != input_state_nodes.end(); ++input_state_node_iter) { XMLNode* input_state_node = *input_state_node_iter; XMLProperty* prop = NULL; if (input_state_node->name() != "input") { continue; } MidiPortState 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 ()); if ((prop = input_state_node->property ("scene-connected")) == 0) { continue; } input_state.scene_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); } } else if (state_node->name() == "MidiOutputs") { XMLNodeList output_state_nodes = state_node->children(); XMLNodeConstIterator output_state_node_iter = output_state_nodes.begin(); _midi_outputs.clear(); for (; output_state_node_iter != output_state_nodes.end(); ++output_state_node_iter) { XMLNode* output_state_node = *output_state_node_iter; XMLProperty* prop = NULL; if (output_state_node->name() != "output") { continue; } MidiPortState output_state (output_state_node->name() ); if ((prop = output_state_node->property ("name")) == 0) { continue; } output_state.name = prop->value(); if ((prop = output_state_node->property ("active")) == 0) { continue; } output_state.active = string_is_affirmative (prop->value ()); if ((prop = output_state_node->property ("scene-connected")) == 0) { continue; } output_state.scene_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); } } } } void EngineStateController::_serialize_engine_states(XMLNode* audio_midi_settings_node) { 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" ); 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); } void EngineStateController::_serialize_midi_port_states(XMLNode* audio_midi_settings_node) { if (!audio_midi_settings_node) { return; } // clean up state data first audio_midi_settings_node->remove_nodes_and_delete("MidiStates" ); XMLNode* midi_states_node = new XMLNode("MidiStates" ); XMLNode* midi_input_states_node = new XMLNode("MidiInputs" ); MidiPortStateList::const_iterator midi_input_state_iter = _midi_inputs.begin(); for (; midi_input_state_iter != _midi_inputs.end(); ++midi_input_state_iter) { XMLNode* midi_input_node = new XMLNode("input" ); 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 ("scene_connected", midi_input_state_iter->scene_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); XMLNode* midi_output_states_node = new XMLNode("MidiOutputs" ); MidiPortStateList::const_iterator midi_output_state_iter = _midi_outputs.begin(); for (; midi_output_state_iter != _midi_outputs.end(); ++midi_output_state_iter) { 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 ("scene_connected", midi_output_state_iter->scene_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); audio_midi_settings_node->add_child_nocopy(*midi_states_node); } 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::_do_initial_engine_setup() { bool state_applied = false; // if we have no saved state load default values if (!_states.empty() ) { // 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() ) { if (!set_new_backend_as_current(backends.front()->name ) ) { std::cerr << "\tfailed to set backend [" << backends.front()->name << "]\n"; } } } } bool EngineStateController::_validate_current_device_state() { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); // check if device parameters from the state record are still valid // validate sample rate std::vector sample_rates = backend->available_sample_rates (_current_state->device_name); if (sample_rates.empty() ) { return false; } // check if session desired sample rate (if it's set) could be used with this device if (_session != 0) { if ( !set_new_sample_rate_in_controller (_session->nominal_frame_rate ()) ) { if ( !set_new_sample_rate_in_controller (backend->default_sample_rate() ) ) { if (!set_new_sample_rate_in_controller (sample_rates.front() ) ) { return false; } } } } else { // check if current sample rate is supported because we have no session desired sample rate value if ( !set_new_sample_rate_in_controller (_current_state->sample_rate) ) { if ( !set_new_sample_rate_in_controller (backend->default_sample_rate() ) ) { if (!set_new_sample_rate_in_controller (sample_rates.front() ) ) { return false; } } } } // validate buffer size std::vector buffer_sizes = backend->available_buffer_sizes (_current_state->device_name); // check if buffer size is supported std::vector::iterator bs_iter = std::find (buffer_sizes.begin(), buffer_sizes.end(), _current_state->buffer_size); // if current is not found switch to default if is supported if (bs_iter == buffer_sizes.end() ) { bs_iter = std::find (buffer_sizes.begin(), buffer_sizes.end(), backend->default_buffer_size () ); if (bs_iter != buffer_sizes.end() ) { _current_state->buffer_size = backend->default_buffer_size (); } else { if (!buffer_sizes.empty() ) { _current_state->buffer_size = buffer_sizes.front(); } } } return true; } const std::string& EngineStateController::get_current_backend_name() const { return _current_state->backend_name; } const std::string& EngineStateController::get_current_device_name() const { return _current_state->device_name; } void EngineStateController::available_backends(std::vector& available_backends) { available_backends = AudioEngine::instance()->available_backends(); } void EngineStateController::enumerate_devices (std::vector& device_vector) const { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); device_vector = backend->enumerate_devices(); } framecnt_t EngineStateController::get_current_sample_rate() const { return _current_state->sample_rate; } framecnt_t EngineStateController::get_default_sample_rate() const { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); return backend->default_sample_rate(); } void EngineStateController::available_sample_rates_for_current_device(std::vector& sample_rates) const { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); sample_rates = backend->available_sample_rates (_current_state->device_name); } uint32_t EngineStateController::get_current_buffer_size() const { return _current_state->buffer_size; } uint32_t EngineStateController::get_default_buffer_size() const { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); return backend->default_buffer_size(); } void EngineStateController::available_buffer_sizes_for_current_device(std::vector& buffer_sizes) const { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); buffer_sizes = backend->available_buffer_sizes (_current_state->device_name); } bool EngineStateController::set_new_backend_as_current(const std::string& backend_name) { if (backend_name == AudioEngine::instance()->current_backend_name () ) { return true; } boost::shared_ptr backend = AudioEngine::instance()->set_backend (backend_name, PROGRAM_NAME, ""); 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; _validate_current_device_state(); } else { // create new record for this engine with default device _current_state = boost::shared_ptr(new State() ); _current_state->backend_name = backend_name; _current_state->device_name = "None"; _validate_current_device_state(); _states.push_front(_current_state); } push_current_state_to_backend(false); return true; } return false; } bool EngineStateController::set_new_device_as_current(const std::string& device_name) { if (_current_state->device_name == device_name) { return true; } boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); std::vector device_vector = backend->enumerate_devices(); // validate the device std::vector::iterator device_iter; device_iter = std::find_if (device_vector.begin(), device_vector.end(), DevicePredicate(device_name) ); // device is available if (device_iter != device_vector.end() ) { boost::shared_ptr previous_state (_current_state); // 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) ); if (found_state_iter != _states.end() ) { // we found a record for current engine and provided device name - switch to it _current_state = *found_state_iter; if (!_validate_current_device_state() ) { _current_state = previous_state; return false; } } else { // the record is not found, create new one _current_state = boost::shared_ptr(new State() ); _current_state->backend_name = backend->name(); _current_state->device_name = device_name; if (!_validate_current_device_state() ) { _current_state = previous_state; return false; } _states.push_front(_current_state); } if (previous_state != NULL) { previous_state->active = false; } push_current_state_to_backend(false); _last_used_real_device.clear(); if (device_name != "None") { _last_used_real_device = device_name; } return true; } // device is not supported by current backend return false; } bool EngineStateController::set_new_sample_rate_in_controller(framecnt_t sample_rate) { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); std::vector sample_rates = backend->available_sample_rates (_current_state->device_name); std::vector::iterator iter = std::find (sample_rates.begin(), sample_rates.end(), (float)sample_rate); if (iter != sample_rates.end() ) { _current_state->sample_rate = sample_rate; return true; } return false; } bool EngineStateController::set_new_buffer_size_in_controller(pframes_t buffer_size) { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); std::vector buffer_sizes = backend->available_buffer_sizes (_current_state->device_name); std::vector::iterator iter = std::find (buffer_sizes.begin(), buffer_sizes.end(), buffer_size); if (iter != buffer_sizes.end() ) { _current_state->buffer_size = buffer_size; return true; } return false; } uint32_t EngineStateController::get_available_inputs_count() const { uint32_t available_channel_count = 0; PortStateList::const_iterator iter = _current_state->input_channel_states.begin(); for (; iter != _current_state->input_channel_states.end(); ++iter) { if (iter->active) { ++available_channel_count; } } return available_channel_count; } uint32_t EngineStateController::get_available_outputs_count () const { uint32_t available_channel_count = 0; PortStateList* output_states; if (Config->get_output_auto_connect() & AutoConnectMaster) { output_states = &_current_state->stereo_out_channel_states; } else { output_states = &_current_state->multi_out_channel_states; } PortStateList::const_iterator iter = output_states->begin(); for (; iter != output_states->end(); ++iter) { if (iter->active) { ++available_channel_count; } } return available_channel_count; } void EngineStateController::get_physical_audio_inputs(std::vector& port_names) { port_names.clear(); PortStateList &input_states = _current_state->input_channel_states; PortStateList::iterator iter = input_states.begin(); for (; iter != input_states.end(); ++iter) { if (iter->active) { port_names.push_back(iter->name); } } } void EngineStateController::get_physical_audio_outputs(std::vector& port_names) { port_names.clear(); PortStateList* output_states; if (Config->get_output_auto_connect() & AutoConnectMaster) { output_states = &_current_state->stereo_out_channel_states; } else { output_states = &_current_state->multi_out_channel_states; } PortStateList::iterator iter = output_states->begin(); for (; iter != output_states->end(); ++iter) { if (iter->active) { port_names.push_back(iter->name); } } } void EngineStateController::get_physical_midi_inputs (std::vector& port_names) { port_names.clear(); MidiPortStateList::iterator iter = _midi_inputs.begin(); for (; iter != _midi_inputs.end(); ++iter) { if (iter->available && iter->active) { port_names.push_back(iter->name); } } } void EngineStateController::get_physical_midi_outputs (std::vector& port_names) { port_names.clear(); MidiPortStateList::iterator iter = _midi_outputs.begin(); for (; iter != _midi_outputs.end(); ++iter) { if (iter->available && iter->active) { port_names.push_back(iter->name); } } } void EngineStateController::set_physical_audio_input_state(const std::string& port_name, bool state) { PortStateList &input_states = _current_state->input_channel_states; PortStateList::iterator found_state_iter; found_state_iter = std::find(input_states.begin(), input_states.end(), PortState(port_name) ); if (found_state_iter != input_states.end() && found_state_iter->active != state ) { found_state_iter->active = state; AudioEngine::instance()->reconnect_session_routes(true, false); InputConfigChanged(); } } void EngineStateController::set_physical_audio_output_state(const std::string& port_name, bool state) { PortStateList* output_states; if (Config->get_output_auto_connect() & AutoConnectMaster) { output_states = &_current_state->stereo_out_channel_states; } else { output_states = &_current_state->multi_out_channel_states; } PortStateList::iterator target_state_iter; target_state_iter = std::find(output_states->begin(), output_states->end(), PortState(port_name) ); if (target_state_iter != output_states->end() && target_state_iter->active != state ) { target_state_iter->active = state; // if StereoOut mode is used if (Config->get_output_auto_connect() & AutoConnectMaster) { // get next element PortStateList::iterator next_state_iter(target_state_iter); // loopback if (++next_state_iter == output_states->end() ) { next_state_iter = output_states->begin(); } // only two outputs should be enabled if (output_states->size() <= 2) { target_state_iter->active = true; next_state_iter->active = true; } else { // if current was set to active - activate next and disable the rest if (target_state_iter->active ) { next_state_iter->active = true; } else { // if current was deactivated but the next is active if (next_state_iter->active) { if (++next_state_iter == output_states->end() ) { next_state_iter = output_states->begin(); } next_state_iter->active = true; } else { // if current was deactivated but the previous is active - restore the state of current target_state_iter->active = true; // state restored; --target_state_iter; // switch to previous to make it stop point in the next cycle target_state_iter->active = true; } } // now deactivate the rest while (++next_state_iter != target_state_iter) { if (next_state_iter == output_states->end() ) { next_state_iter = output_states->begin(); // we jumped, so additional check is required if (next_state_iter == target_state_iter) { break; } } next_state_iter->active = false; } } } AudioEngine::instance()->reconnect_session_routes(false, true); OutputConfigChanged(); } } bool EngineStateController::get_physical_audio_input_state(const std::string& port_name) { bool state = false; PortStateList &input_states = _current_state->input_channel_states; PortStateList::iterator found_state_iter; found_state_iter = std::find(input_states.begin(), input_states.end(), PortState(port_name) ); if (found_state_iter != input_states.end() ) { state = found_state_iter->active; } return state; } bool EngineStateController::get_physical_audio_output_state(const std::string& port_name) { bool state = false; PortStateList* output_states; if (Config->get_output_auto_connect() & AutoConnectMaster) { output_states = &_current_state->stereo_out_channel_states; } else { output_states = &_current_state->multi_out_channel_states; } PortStateList::iterator found_state_iter; found_state_iter = std::find(output_states->begin(), output_states->end(), PortState(port_name) ); if (found_state_iter != output_states->end() ) { state = found_state_iter->active; } return state; } void EngineStateController::set_physical_midi_input_state(const std::string& port_name, bool state) { MidiPortStateList::iterator found_state_iter; found_state_iter = std::find(_midi_inputs.begin(), _midi_inputs.end(), MidiPortState(port_name) ); if (found_state_iter != _midi_inputs.end() && found_state_iter->available && found_state_iter->active != state ) { found_state_iter->active = state; if (_session) { // reconnect MTC inputs as well if (found_state_iter->mtc_in) { _session->reconnect_mtc_ports (); } _session->reconnect_mmc_ports(true); } MIDIInputConfigChanged(); } } void EngineStateController::set_physical_midi_output_state(const std::string& port_name, bool state) { MidiPortStateList::iterator found_state_iter; found_state_iter = std::find(_midi_outputs.begin(), _midi_outputs.end(), MidiPortState(port_name) ); if (found_state_iter != _midi_outputs.end() && found_state_iter->available && found_state_iter->active != state ) { found_state_iter->active = state; if (_session) { _session->reconnect_mmc_ports(false); } MIDIOutputConfigChanged(); } } bool EngineStateController::get_physical_midi_input_state(const std::string& port_name, bool& scene_connected) { bool state = false; MidiPortStateList::iterator found_state_iter; found_state_iter = std::find(_midi_inputs.begin(), _midi_inputs.end(), MidiPortState(port_name) ); if (found_state_iter != _midi_inputs.end() && found_state_iter->available) { state = found_state_iter->active; scene_connected = found_state_iter->scene_connected; } return state; } bool EngineStateController::get_physical_midi_output_state(const std::string& port_name, bool& scene_connected) { bool state = false; MidiPortStateList::iterator found_state_iter; found_state_iter = std::find(_midi_outputs.begin(), _midi_outputs.end(), MidiPortState(port_name) ); if (found_state_iter != _midi_outputs.end() && found_state_iter->available) { state = found_state_iter->active; scene_connected = found_state_iter->scene_connected; } return state; } void EngineStateController::set_physical_midi_scene_in_connection_state(const std::string& port_name, bool state) { MidiPortStateList::iterator found_state_iter; found_state_iter = std::find(_midi_inputs.begin(), _midi_inputs.end(), MidiPortState(port_name) ); if (found_state_iter != _midi_inputs.end() && found_state_iter->available && found_state_iter->active ) { found_state_iter->scene_connected = state; std::vector ports; ports.push_back(port_name); MIDISceneInputConnectionChanged(ports, state); } } void EngineStateController::set_physical_midi_scenen_out_connection_state(const std::string& port_name, bool state) { MidiPortStateList::iterator found_state_iter; found_state_iter = std::find(_midi_outputs.begin(), _midi_outputs.end(), MidiPortState(port_name) ); if (found_state_iter != _midi_outputs.end() && found_state_iter->available && found_state_iter->active ) { found_state_iter->scene_connected = state; std::vector ports; ports.push_back(port_name); MIDISceneOutputConnectionChanged(ports, state); } } void EngineStateController::set_all_midi_scene_inputs_disconnected() { MidiPortStateList::iterator iter = _midi_inputs.begin(); for (; iter != _midi_inputs.end(); ++iter) { iter->scene_connected = false; } std::vector ports; MIDISceneInputConnectionChanged(ports, false); } void EngineStateController::set_all_midi_scene_outputs_disconnected() { MidiPortStateList::iterator iter = _midi_outputs.begin(); for (; iter != _midi_outputs.end(); ++iter) { iter->scene_connected = false; } std::vector ports; MIDISceneOutputConnectionChanged(ports, false); } 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; if (_session) { _session->reconnect_mtc_ports (); } } } MTCInputChanged(port_name); } void EngineStateController::set_state_to_all_inputs(bool state) { bool something_changed = false; PortStateList::iterator iter = _current_state->input_channel_states.begin(); for (; iter != _current_state->input_channel_states.end(); ++iter) { if (iter->active != state) { iter->active = state; something_changed = true; } } if (something_changed) { AudioEngine::instance()->reconnect_session_routes(true, false); InputConfigChanged(); } } void EngineStateController::set_state_to_all_outputs(bool state) { // unapplicable in Stereo Out mode, just return if (Config->get_output_auto_connect() & AutoConnectMaster) { return; } bool something_changed = false; PortStateList::iterator iter = _current_state->multi_out_channel_states.begin(); for (; iter != _current_state->multi_out_channel_states.end(); ++iter) { if (iter->active != state) { iter->active = state; something_changed = true; } } if (something_changed) { AudioEngine::instance()->reconnect_session_routes(false, true); OutputConfigChanged(); } } void EngineStateController::get_physical_audio_input_states(std::vector& channel_states) { PortStateList &input_states = _current_state->input_channel_states; channel_states.assign(input_states.begin(), input_states.end()); } void EngineStateController::get_physical_audio_output_states(std::vector& channel_states) { PortStateList* output_states; if (Config->get_output_auto_connect() & AutoConnectMaster) { output_states = &_current_state->stereo_out_channel_states; } else { output_states = &_current_state->multi_out_channel_states; } channel_states.assign(output_states->begin(), output_states->end()); } void EngineStateController::get_physical_midi_input_states (std::vector& channel_states) { channel_states.clear(); MidiPortStateList::iterator iter = _midi_inputs.begin(); for (; iter != _midi_inputs.end(); ++iter ) { if (iter->available) { MidiPortState state(iter->name); state.active = iter->active; state.available = true; state.scene_connected = iter->scene_connected; state.mtc_in = iter->mtc_in; channel_states.push_back(state); } } } void EngineStateController::get_physical_midi_output_states (std::vector& channel_states) { channel_states.clear(); MidiPortStateList::iterator iter = _midi_outputs.begin(); for (; iter != _midi_outputs.end(); ++iter ) { if (iter->available) { MidiPortState state(iter->name); state.active = iter->active; state.available = true; state.scene_connected = iter->scene_connected; state.mtc_in = iter->mtc_in; channel_states.push_back(state); } } } void EngineStateController::_on_session_loaded () { if (!_session) { return; } AudioEngine::instance()->reconnect_session_routes(true, true); _session->reconnect_mtc_ports (); _session->reconnect_mmc_ports (true); _session->reconnect_mmc_ports (false); framecnt_t desired_sample_rate = _session->nominal_frame_rate (); if ( desired_sample_rate > 0 && set_new_sample_rate_in_controller(desired_sample_rate) ) { push_current_state_to_backend(false); SampleRateChanged(); // emit a signal } } void EngineStateController::_on_sample_rate_change(framecnt_t new_sample_rate) { if (_current_state->sample_rate != new_sample_rate) { // if sample rate has been changed framecnt_t sample_rate_to_set = new_sample_rate; if (AudioEngine::instance()->session() ) { // and we have current session we should restore it back to the one tracks uses sample_rate_to_set = AudioEngine::instance()->session()->frame_rate (); } if ( !set_new_sample_rate_in_controller (sample_rate_to_set) ) { // if sample rate can't be set // switch to NONE device set_new_device_as_current ("None"); DeviceListChanged(false); DeviceError(); } } SampleRateChanged(); // emit a signal } void EngineStateController::_on_buffer_size_change(pframes_t new_buffer_size) { if (_current_state->buffer_size != new_buffer_size) { _current_state->buffer_size = new_buffer_size; } BufferSizeChanged(); // emit a signal } void EngineStateController::_on_device_list_change() { bool current_device_disconnected = false; boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); std::vector device_vector = backend->enumerate_devices(); // find out out if current device is still available if it's not None if (_current_state->device_name != "None") { std::vector::iterator device_iter; device_iter = std::find_if (device_vector.begin(), device_vector.end(), DevicePredicate(_current_state->device_name) ); // if current device is not available any more - switch to None device if (device_iter == device_vector.end() ) { StateList::iterator found_state_iter = find_if (_states.begin(), _states.end(), State::StatePredicate(_current_state->backend_name, "None") ); if (found_state_iter != _states.end() ) { // found the record - switch to it _current_state = *found_state_iter; _validate_current_device_state(); } else { // create new record for this engine with default device _current_state = boost::shared_ptr(new State() ); _current_state->backend_name = backend->name(); _current_state->device_name = "None"; _validate_current_device_state(); _states.push_front(_current_state); } push_current_state_to_backend(true); current_device_disconnected = true; } } else { // if the device which was active before is available now - switch to it std::vector::iterator device_iter; device_iter = std::find_if (device_vector.begin(), device_vector.end(), DevicePredicate(_last_used_real_device) ); if (device_iter != device_vector.end() ) { StateList::iterator found_state_iter = find_if (_states.begin(), _states.end(), State::StatePredicate(_current_state->backend_name, _last_used_real_device) ); if (found_state_iter != _states.end() ) { boost::shared_ptr previous_state (_current_state); _current_state = *found_state_iter; if (_validate_current_device_state() ) { push_current_state_to_backend(false); } else { // cannot use this device right now _last_used_real_device.clear(); _current_state = previous_state; } } } } DeviceListChanged(current_device_disconnected); // emit a signal } void EngineStateController::_update_device_channels_state() { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); assert(backend); // update audio input states std::vector phys_audio_inputs; backend->get_physical_inputs(DataType::AUDIO, phys_audio_inputs); PortStateList new_input_states; PortStateList &input_states = _current_state->input_channel_states; std::vector::const_iterator input_iter = phys_audio_inputs.begin(); for (; input_iter != phys_audio_inputs.end(); ++input_iter) { PortState state(*input_iter); state.active = true; PortStateList::const_iterator found_state_iter = std::find(input_states.begin(), input_states.end(), state); if (found_state_iter != input_states.end() ) { new_input_states.push_back(*found_state_iter); } else { new_input_states.push_back(state); } } _current_state->input_channel_states = new_input_states; // update audio output states (multi out mode) std::vector phys_audio_outputs; backend->get_physical_outputs(DataType::AUDIO, phys_audio_outputs); PortStateList new_output_states; PortStateList &output_multi_states = _current_state->multi_out_channel_states; std::vector::const_iterator output_iter = phys_audio_outputs.begin(); for (; output_iter != phys_audio_outputs.end(); ++output_iter) { PortState state(*output_iter); state.active = true; PortStateList::const_iterator found_state_iter = std::find(output_multi_states.begin(), output_multi_states.end(), state); if (found_state_iter != output_multi_states.end() ) { new_output_states.push_back(*found_state_iter); } else { new_output_states.push_back(state); } } _current_state->multi_out_channel_states = new_output_states; // update audio output states (stereo out mode) new_output_states.clear(); PortStateList &output_stereo_states = _current_state->stereo_out_channel_states; output_iter = phys_audio_outputs.begin(); for (; output_iter != phys_audio_outputs.end(); ++output_iter) { PortState state(*output_iter); state.active = true; PortStateList::const_iterator found_state_iter = std::find(output_stereo_states.begin(), output_stereo_states.end(), state); if (found_state_iter != output_stereo_states.end() ) { new_output_states.push_back(*found_state_iter); } else { new_output_states.push_back(state); } } _current_state->stereo_out_channel_states = new_output_states; _refresh_stereo_out_channel_states(); // update midi ports: unlike audio ports which states are saved per device // each midi port state is saved individualy // so get all midi ports from the backend // and compare to the list of midi ports we have // if physical port is new, add it to our state list // if physical port is present in our state list - mark it available // if there is no corresponding physical port to one we have in a list - leave it unavailable MidiPortStateList::iterator iter = _midi_inputs.begin(); for (; iter != _midi_inputs.end(); ++iter) { iter->available = false; } for (iter = _midi_outputs.begin(); iter != _midi_outputs.end(); ++iter) { iter->available = false; } // update midi input ports std::vector phys_midi_inputs; backend->get_physical_inputs(DataType::MIDI, phys_midi_inputs); std::vector::const_iterator midi_input_iter = phys_midi_inputs.begin(); for (; midi_input_iter != phys_midi_inputs.end(); ++midi_input_iter) { MidiPortState state(*midi_input_iter); state.active = false; state.available = true; MidiPortStateList::iterator found_state_iter = std::find(_midi_inputs.begin(), _midi_inputs.end(), state); if (found_state_iter != _midi_inputs.end() ) { found_state_iter->available = true; } else { _midi_inputs.push_back(state); } } // update midi output ports std::vector phys_midi_outputs; backend->get_physical_outputs(DataType::MIDI, phys_midi_outputs); std::vector::const_iterator midi_output_iter = phys_midi_outputs.begin(); for (; midi_output_iter != phys_midi_outputs.end(); ++midi_output_iter) { MidiPortState state(*midi_output_iter); state.active = false; state.available = true; MidiPortStateList::iterator found_state_iter = std::find(_midi_outputs.begin(), _midi_outputs.end(), state); if (found_state_iter != _midi_outputs.end() ) { found_state_iter->available = true; } else { _midi_outputs.push_back(state); } } } void EngineStateController::_refresh_stereo_out_channel_states() { PortStateList &output_states = _current_state->stereo_out_channel_states; PortStateList::iterator active_iter = output_states.begin(); for (; active_iter != output_states.end(); ++active_iter) { if (active_iter->active) { break; } } uint32_t pending_active_channels = 2; PortStateList::iterator iter = output_states.begin(); // if found active if (active_iter != output_states.end() ) { iter = active_iter; if (++iter == output_states.end() ) { iter = output_states.begin(); } (iter++)->active = true; pending_active_channels = 0; } // drop the rest of the states to false (until we reach the end or first existing active channel) for (; iter != output_states.end() && iter != active_iter; ++iter) { if (pending_active_channels) { iter->active = true; --pending_active_channels; } else { iter->active = false; } } } void EngineStateController::_on_engine_running () { AudioEngine::instance()->reconnect_session_routes(true, true); _current_state->active = true; EngineRunning(); // emit a signal } void EngineStateController::_on_engine_stopped () { EngineStopped(); } void EngineStateController::_on_engine_halted () { EngineHalted(); } void EngineStateController::_on_device_error() { set_new_device_as_current ("None"); push_current_state_to_backend(true); DeviceListChanged(false); DeviceError(); } void EngineStateController::_on_parameter_changed (const std::string& parameter_name) { if (parameter_name == "output-auto-connect") { AudioEngine::instance()->reconnect_session_routes(false, true); OutputConfigChanged(); // emit a signal OutputConnectionModeChanged(); // emit signal } } void EngineStateController::_on_ports_registration_update () { _update_device_channels_state(); // update MIDI connections if (_session) { _session->reconnect_midi_scene_ports (true); _session->reconnect_midi_scene_ports (false); _session->reconnect_mtc_ports (); _session->reconnect_mmc_ports (true); _session->reconnect_mmc_ports (false); } PortRegistrationChanged(); // emit a signal } bool EngineStateController::push_current_state_to_backend(bool start) { boost::shared_ptr backend = AudioEngine::instance()->current_backend(); if (!backend) { return false; } // check if anything changed bool state_changed = (_current_state->device_name != backend->device_name() ) || (_current_state->sample_rate != backend->sample_rate() ) || (_current_state->buffer_size != backend->buffer_size() ); bool was_running = AudioEngine::instance()->running(); Glib::Threads::RecMutex::Lock sl (AudioEngine::instance()->state_lock() ); if (state_changed) { if (was_running) { if (AudioEngine::instance()->stop () ) { return false; } } int result = 0; { std::cout << "EngineStateController::Setting device: " << _current_state->device_name << std::endl; if ((_current_state->device_name != backend->device_name()) && (result = backend->set_device_name (_current_state->device_name)) ) { error << string_compose (_("Cannot set device name to %1"), get_current_device_name()) << endmsg; } if (!result ) { std::cout << "EngineStateController::Setting device sample rate " << _current_state->sample_rate << std::endl; result = backend->set_sample_rate (_current_state->sample_rate); if (result) { error << string_compose (_("Cannot set sample rate to %1"), get_current_sample_rate()) << endmsg; } } if (!result ) { std::cout << "EngineStateController::Setting device buffer size " << _current_state->buffer_size << std::endl; result = backend->set_buffer_size (_current_state->buffer_size); if (result) { error << string_compose (_("Cannot set buffer size to %1"), get_current_buffer_size()) << endmsg; } } } if (result) // error during device setup { //switch to None device and notify about the issue set_new_device_as_current ("None"); DeviceListChanged(false); DeviceError(); } if (AudioEngine::instance()->backend_reset_requested() ) { // device asked for reset, do not start engine now // free sate lock and let Engine reset the device as it's required return true; } } if(start || (was_running && state_changed) ) { if (AudioEngine::instance()->start () && !AudioEngine::instance()->is_reset_requested() ) { //switch to None device and notify about the issue set_new_device_as_current ("None"); AudioEngine::instance()->start (); DeviceListChanged(false); DeviceError(); return false; } } save_audio_midi_settings(); return true; }