/* 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. */ #ifndef __gtk2_ardour__engine_state_controller__ #define __gtk2_ardour__engine_state_controller__ #include #include #include "ardour/types.h" #include "ardour/audio_backend.h" namespace ARDOUR { class AudioBackendInfo; /** * @class EngineStateController * @brief EngineStateController class. * * Implements usecases for Audio devices and Audio/Midi ports. * Persistantly saves to the config device configuration settings and audio/midi port states */ class EngineStateController { public: // public data types: /** * @struct PortState * Structure which represents AudioPort state */ struct PortState { std::string name; ///< Audio Port name bool active; ///< Audio Port state PortState () : name("") , active(false) { } PortState (const std::string& name) : name(name) , active(false) { } bool operator==(const PortState& rhs) {return rhs.name == name; } }; /// @typedef Type for the list of all available audio ports typedef std::list PortStateList; /** * @struct MidiPortState * Structure which represents MidiPort state. */ struct MidiPortState { std::string name; ///< Midi Port name bool active; ///< Midi Port state bool available; ///< Midi Port availability - if it is physicaly available or not bool scene_connected; ///< Is midi port used for scene MIDI marker in/out bool mtc_in; ///< Is midi port used as MTC in MidiPortState(const std::string& name) : name(name) , active(false) , available(false) , scene_connected(false) , mtc_in(false) {} bool operator==(const MidiPortState& rhs) { return name == rhs.name; } }; /// @typedef Type for the list of MidiPorts ever registered in the system typedef std::list MidiPortStateList; //Interfaces /** Get an instance of EngineStateController singleton. * @return EngineStateController instance pointer */ static EngineStateController* instance (); /** Associate session with EngineStateController instance. */ void set_session (Session* session); /** Remove link to the associated session. */ void remove_session (); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // General backend/device information methods /** Provides names of all available backends. * * @param[out] available_backends - vector of available backends */ void available_backends (std::vector& available_backends); /** Provides the name of currently used backend. * * @return the name of currently used backend */ const std::string& get_current_backend_name() const; /** Provides the name of currently used device. * * @return the name of currently used device */ const std::string& get_current_device_name () const; /** Provides names for all available devices. * * @param[out] device_vector - vector of available devices */ void enumerate_devices (std::vector& device_vector) const; /** Get sample rate used by current device. * * @return current sample rate */ ARDOUR::framecnt_t get_current_sample_rate () const; /** Get default sample rate for current backend. * * @return default sample rate for current backend */ ARDOUR::framecnt_t get_default_sample_rate () const; /** Get sample rates which are supported by current device and current backend. * * @param[out] sample_rates - vector of supported sample rates */ void available_sample_rates_for_current_device (std::vector& sample_rates) const; /** Get buffer size used by current device. * * @return current buffer size */ ARDOUR::pframes_t get_current_buffer_size () const; /** Get default buffer size for current backend. * * @return default buffer size for current backend */ ARDOUR::pframes_t get_default_buffer_size () const; /** Get buffer sizes which are supported by current device and current backend. * * @param[out] buffer_sizes - vector of supported buffer_sizes */ void available_buffer_sizes_for_current_device (std::vector& buffer_sizes) const; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // device state control methods /** Get the number of all enabled Audio inputs. * * @return number of all enabled Audio inputs */ uint32_t get_available_inputs_count() const; /** Get the number of all enabled Audio outputs. * * @return number of all enabled Audio outputs */ uint32_t get_available_outputs_count () const; /** Get vector of all enabled physical Audio input port names. * * @param[out] port_names - vector of all enabled Audio input names */ void get_physical_audio_inputs (std::vector& port_names); /** Get vector of all enabled physical Audio input port names. * * @param[out] port_names - vector of all enabled Audio input names */ void get_physical_audio_outputs (std::vector& port_names); /** Get vector of all enabled physical MIDI input port names. * * @param[out] port_names - vector of all enabled MIDI input names */ void get_physical_midi_inputs (std::vector& port_names); /** Get vector of all enabled physical MIDI output port names. * * @param[out] port_names - vector of all enabled MIDI output names */ void get_physical_midi_outputs (std::vector& port_names); /** Sets new state to all Audio inputs. * * @param[in] state - new state */ void set_state_to_all_inputs(bool state); /** Sets new state to all Audio outputs. * @note Does nothing in Stereo Out mode * @param[in] state - new state */ void set_state_to_all_outputs(bool state); /** Get vector of states for all physical Audio input ports. * * @param[out] channel_states - vector of input port states */ void get_physical_audio_input_states(std::vector& channel_states); /** Get vector of states for all physical Audio output ports. * * @param[out] channel_states - vector of output port states */ void get_physical_audio_output_states(std::vector& channel_states); /** Set state of the specified Audio input port. * * @param[in] port_name - input name * @param[in] state - new state */ void set_physical_audio_input_state(const std::string& port_name, bool state); /** Set state of the specified Audio output port. * * @param[in] port_name - output name * @param[in] state - new state */ void set_physical_audio_output_state(const std::string& port_name, bool state); /** Get state of the specified Audio input port. * * @param[in] port_name - input name * @return input state */ bool get_physical_audio_input_state(const std::string& port_name); /** Get state of the specified Audi output port. * * @param[in] port_name - output name * @return output state */ bool get_physical_audio_output_state(const std::string& port_name); /** Get vector of all enabled MIDI input port names. * * @param[out] channel_states - vector of enabled inputs */ void get_physical_midi_input_states (std::vector& channel_states); /** Get vector of all enabled MIDI output port names. * * @param[out] channel_states - vector of enabled outputs */ void get_physical_midi_output_states (std::vector& channel_states); /** Get name of mtc source port * * return name of mtc source port */ std::string get_mtc_source_port (); /** Set ltc source port * * @param[in] port - name of ltc source port */ void set_ltc_source_port (const std::string& port); /** Get name of ltc source port * * return name of ltc source port */ std::string get_ltc_source_port (); /** Set ltc output port * * @param[in] port - name of ltc output port */ void set_ltc_output_port (const std::string&); /** Get name of ltc output port * * return name of ltc output port */ std::string get_ltc_output_port (); /** Set state of the specified MIDI input port. * * @param[in] port_name - input name * @param[in] state - new state */ void set_physical_midi_input_state(const std::string& port_name, bool state); /** Set state of the specified MIDI output port. * * @param[in] port_name - output name * @param[in] state - new state */ void set_physical_midi_output_state(const std::string& port_name, bool state); /** Get state of the specified MIDI input port. * * @param[in] port_name - input name * @param[out] scene_connected - is port used as Scene In or not * @return input state */ bool get_physical_midi_input_state(const std::string& port_name, bool& scene_connected); /** Get state of the specified MIDI output port. * * @param[in] port_name - output name * @param[out] scene_connected - is port used as Scene Out or not * @return output state */ bool get_physical_midi_output_state(const std::string& port_name, bool& scene_connected); /** Set state of Scene In connection for the specified MIDI input port. * * @param[in] port_name - input name * @param[in] state - new state */ void set_physical_midi_scene_in_connection_state(const std::string& port_name, bool state); /** Set state of Scene Out connection for the specified MIDI output port. * * @param[in] port_name - input name * @param[in] state - new state */ void set_physical_midi_scenen_out_connection_state(const std::string&, bool); /** Disocnnect all MIDI input ports from Scene In. */ void set_all_midi_scene_inputs_disconnected(); /** Disocnnect all MIDI output ports from Scene Out. */ void set_all_midi_scene_outputs_disconnected(); /** Set MIDI TimeCode input port * @note There is a sense to choose MIDI TimeCode input only because * our MIDI TimeCode is propagated to all midi output ports. */ void set_mtc_source_port (const std::string&); /** Check if AudioEngine setup is required * @return true if setup is required, otherwise - false */ bool is_setup_required() const {return ARDOUR::AudioEngine::instance()->setup_required (); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Methods set parameters inside the controller // the state of engine won't change untill we make a "push" of this state to the backend // NOTE: Use push_state_to_backend() method to update backend with the most recent controller state /** Set new sample rate for current device in EngineStateController database * @note Use push_state_to_backend() method to update backend/device state with the most recent controller state * @param sample_rate - new sample rate */ bool set_new_sample_rate_in_controller(framecnt_t sample_rate); /** Set new buffer size for current device in EngineStateController database * @note Use push_state_to_backend() method to update backend/device state with the most recent controller state * @param buffer_size - new buffer size */ bool set_new_buffer_size_in_controller(pframes_t buffer_size); /** @brief push current controller state to backend. * Propagate and set all current EngineStateController parameters to the backend * @note Engine will be restarted if it's running when this method is called. * @note If an attempt ot set parameters is unsuccessful current device will be switched to "None". * @param start - start the Engine if it was not running when this function was called. * @return true on success, otherwise - false */ bool push_current_state_to_backend(bool start); /** Switch to new backend * @note The change will be propagated emmidiatelly as if push_current_state_to_backend () was called. * @param backend_name - new backend name. * @return true on success, otherwise - false */ bool set_new_backend_as_current(const std::string& backend_name); /** Switch to new device * @note The change will be propagated emmidiatelly as if push_current_state_to_backend () was called. * @param device_name - new device name. * @return true on success, otherwise - false */ bool set_new_device_as_current(const std::string& device_name); //////////////////////////////////////////////////////////////////////////////////////////////////// // Methods to save/serialize setting states /** Serialize Audio/Midi settings (entire EngineStateController database) to XML * @return XML note with serialized states */ XMLNode& serialize_audio_midi_settings(); /** Save Audio/Midi settings (entire EngineStateController database) to config persistently */ void save_audio_midi_settings(); //////////////////////////////////////////////////////////////////////////////////////////////////// //UPDATE SIGNALS /** This signal is emitted if the sample rate changes */ PBD::Signal0 SampleRateChanged; /** This signal is emitted if the buffer size changes */ PBD::Signal0 BufferSizeChanged; /** This signal is emitted if the device list changes */ PBD::Signal1 DeviceListChanged; /** This signal is emitted if the device cannot operate properly */ PBD::Signal0 DeviceError; //////////////////////////////////////////////////////////////////////////////////////////////////// //ENGINE STATE SIGNALS /** This signal is emitted when the engine is started */ PBD::Signal0 EngineRunning; /** This signal is emitted when the engine is stopped */ PBD::Signal0 EngineStopped; /** This signal is emitted if Engine processing is terminated */ PBD::Signal0 EngineHalted; /** This signal is emitted if the AUDIO input channel configuration changes */ PBD::Signal0 InputConfigChanged; /** This signal is emitted if the AUDIO output channel configuration changes */ PBD::Signal0 OutputConfigChanged; /** This signal is emitted if the AUDIO output connection mode changes * @note By output connection mode "Stereo Out" or "Multi Out" is meant */ PBD::Signal0 OutputConnectionModeChanged; /** This signals is emitted if the MIDI input channel configuration changes */ PBD::Signal0 MIDIInputConfigChanged; /** This signals is emitted if the MIDI output channel configuration changes */ PBD::Signal0 MIDIOutputConfigChanged; /** This signals is emitted if the MIDI Scene In connection changes */ PBD::Signal2&, bool> MIDISceneInputConnectionChanged; /** This signals is emitted if the MIDI Scene Out connection changes */ PBD::Signal2&, bool> MIDISceneOutputConnectionChanged; /** This signal is emitted if the MTC Input channel is changed */ PBD::Signal1 MTCInputChanged; /** This signal is emitted if new Audio/MIDI ports are registered or unregistered */ PBD::Signal0 PortRegistrationChanged; private: EngineStateController(); /// singleton ~EngineStateController(); /// singleton EngineStateController(const EngineStateController& ); /// prohibited EngineStateController& operator=(const EngineStateController&); /// prohibited //////////////////////////////////////////////////////////////////////////////////////////// // private data structures /** @struct Engine state * @brief State structure. * Contains information about single device/backend state */ struct State { std::string backend_name; ///< state backend name std::string device_name; ///< state device name ARDOUR::framecnt_t sample_rate; ///< sample rate used by the device in this state ARDOUR::pframes_t buffer_size; ///< buffer size used by the device in this state PortStateList input_channel_states; ///< states of device Audio inputs PortStateList multi_out_channel_states; ///< states of device Audio inputs in Multi Out mode PortStateList stereo_out_channel_states; ///< states of device Audio inputs in Stereo Out mode bool active; ///< was this state the most recent active one State() : sample_rate(0) , buffer_size(0) , input_channel_states (0) , multi_out_channel_states (0) , stereo_out_channel_states (0) , active (false) { } bool operator==(const State& rhs) { return (backend_name == rhs.backend_name) && (device_name == rhs.device_name); } /** Forms string name for the state * @return name string */ std::string form_state_name() { return std::string("State:" + backend_name + ":" + device_name); } /** @struct StatepPredicate * This predicate is used to identify a state during search in the list of states */ struct StatePredicate { StatePredicate(const std::string& backend_name, const std::string& device_name) : _backend_name (backend_name) , _device_name (device_name) {} bool operator()(boost::shared_ptr rhs) { return (_backend_name == rhs->backend_name) && (_device_name == rhs->device_name); } private: std::string _backend_name; std::string _device_name; }; }; /// @typedef Type for the state pointer typedef boost::shared_ptr StatePtr; /// @typedef Type for the list of states typedef std::list StateList; //////////////////////////////////////////////////////////////////////////////////////////////////// // methods to manage setting states /** Deserializes and loads Engine and Audio port states from the config to EngineStateController */ void _deserialize_and_load_engine_states(); /** Deserializes and loads MIDI port states from the config to EngineStateController */ void _deserialize_and_load_midi_port_states(); /** Serializes Engine and Audio port states from EngineStateController to XML node * @param[in,out] audio_midi_settings_node - node to serialize the satets to */ void _serialize_engine_states(XMLNode* audio_midi_settings_node); /** Serializes MIDI port states from EngineStateController to XML node * @param[in,out] audio_midi_settings_node - node to serialize the satets to */ void _serialize_midi_port_states(XMLNode* audio_midi_settings_node); /** Provides initial state configuration. * It loades the last active state if there is one and it is aplicable. * Otherwise default state (None device with default sample rate and buffer size) is loaded. */ void _do_initial_engine_setup(); /** Loades provided state. * @note It's possible that provided state can't be loaded * (device disconnected or reqested parameters are not supported anymore). * @param state - state to apply * @return true on success, otherwise - false */ bool _apply_state(const StatePtr& state); /** Gets available device channels from engine and updates internal controller state */ void _update_device_channels_state(); /** Check "Stereo Out" mode channels state configuration and make it correspond Stereo Out mode requirements */ void _refresh_stereo_out_channel_states(); //////////////////////////////////////////////////////////////////////////////////////////////////////////// // internal helper functions /** make sure that current device parameters are supported and fit session requirements * @return true if current state is valid and all parameters are supported, otherwise - false */ bool _validate_current_device_state(); /** change ltc source port in case of the input ports list change */ void _update_ltc_source_port (); /** change ltc source port in case of the output ports list change */ void _update_ltc_output_port (); /** check that port is still existed in the list of input ports @param[in] port - port name @return true if current port is existed, otherwise - false */ bool _audio_input_port_exists (const std::string& port); /** check that port is still existed in the list of output ports @param[in] port - port name @return true if current port is existed, otherwise - false */ bool _audio_output_port_exists (const std::string& port); //////////////////////////////////////// // callbacks /** Invoked when Engine starts running */ void _on_engine_running(); /** Invoked when Engine is halted */ void _on_engine_halted(); /** Invoked when Engine processing is terminated */ void _on_engine_stopped(); /** Invoked when Device error accured, it failed to start or didn't accept the change which should */ void _on_device_error(); /** Invoked when current device changes sample rate */ void _on_sample_rate_change(ARDOUR::framecnt_t); /** Invoked when current device changes buffer size */ void _on_buffer_size_change(ARDOUR::pframes_t); /** Invoked when the list of available devices is changed */ void _on_device_list_change(); /** Invoked when the config parameter is changed */ void _on_parameter_changed (const std::string&); /** Invoked when Audio/MIDI ports are registered or unregistered */ void _on_ports_registration_update (); /** Invoked when session loading process is complete */ void _on_session_loaded(); //////////////////////////////////////// //////////////////////////////////////// // attributes StatePtr _current_state; ///< current state pointer // list of system states StateList _states; ///< list of all available states MidiPortStateList _midi_inputs; ///< midi input states MidiPortStateList _midi_outputs; ///< midi output states std::string _last_used_real_device; ///< last active non-default (real) device Session* _session; ///< current session // Engine connections stuff PBD::ScopedConnectionList update_connections; ///< connection container for update signals PBD::ScopedConnectionList session_connections; ///< connection container for session signals PBD::ScopedConnection running_connection; ///< connection container for EngineRunning signal PBD::ScopedConnection halt_connection; ///< connection container for EngineHalted signal PBD::ScopedConnection stopped_connection; ///< connection container for EngineStopped signal }; } // namespace ARDOUR #endif /* defined(__gtk2_ardour__engine_state_controller__) */