mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-09 08:14:58 +01:00
Implement restoring hardware<>hardware connections for internal backends
This commit is contained in:
parent
4a51f4f350
commit
94a4f6b350
14 changed files with 248 additions and 0 deletions
|
|
@ -501,6 +501,13 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual samplepos_t sample_time_at_cycle_start () = 0;
|
virtual samplepos_t sample_time_at_cycle_start () = 0;
|
||||||
|
|
||||||
|
/* external connections (hardware <> hardware)
|
||||||
|
* for internal backends
|
||||||
|
*/
|
||||||
|
virtual XMLNode* get_state () const { return nullptr; }
|
||||||
|
virtual int set_state (XMLNode const&, int version) { return 0; }
|
||||||
|
virtual bool match_state (XMLNode const&, int version) { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PortManager& manager;
|
PortManager& manager;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,9 @@ protected:
|
||||||
|
|
||||||
virtual BackendPort* port_factory (std::string const& name, ARDOUR::DataType dt, ARDOUR::PortFlags flags) = 0;
|
virtual BackendPort* port_factory (std::string const& name, ARDOUR::DataType dt, ARDOUR::PortFlags flags) = 0;
|
||||||
|
|
||||||
|
XMLNode* get_state () const;
|
||||||
|
int set_state (XMLNode const&, int version);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
void list_ports () const;
|
void list_ports () const;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ CONFIG_VARIABLE (Temporal::TimeDomain, preferred_time_domain, "preferred_time_do
|
||||||
|
|
||||||
/* IO connection */
|
/* IO connection */
|
||||||
|
|
||||||
|
CONFIG_VARIABLE (bool, restore_hardware_connections, "restore-hardware-connections", true)
|
||||||
CONFIG_VARIABLE (bool, auto_connect_standard_busses, "auto-connect-standard-busses", true)
|
CONFIG_VARIABLE (bool, auto_connect_standard_busses, "auto-connect-standard-busses", true)
|
||||||
/* this variable is used to indicate output mode in Waves Tracks:
|
/* this variable is used to indicate output mode in Waves Tracks:
|
||||||
"Multi Out" == AutoConnectPhysical and "Stereo Out" == AutoConnectMaster
|
"Multi Out" == AutoConnectPhysical and "Stereo Out" == AutoConnectMaster
|
||||||
|
|
|
||||||
|
|
@ -2084,6 +2084,8 @@ private:
|
||||||
XMLNode* _bundle_xml_node;
|
XMLNode* _bundle_xml_node;
|
||||||
int load_bundles (XMLNode const &);
|
int load_bundles (XMLNode const &);
|
||||||
|
|
||||||
|
mutable XMLNode* _engine_state;
|
||||||
|
|
||||||
int backend_sync_callback (TransportState, samplepos_t);
|
int backend_sync_callback (TransportState, samplepos_t);
|
||||||
|
|
||||||
void process_rtop (SessionEvent*);
|
void process_rtop (SessionEvent*);
|
||||||
|
|
|
||||||
|
|
@ -839,6 +839,54 @@ PortEngineSharedImpl::process_connection_queue_locked (PortManager& mgr)
|
||||||
_port_connection_queue.clear ();
|
_port_connection_queue.clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XMLNode*
|
||||||
|
PortEngineSharedImpl::get_state () const
|
||||||
|
{
|
||||||
|
XMLNode* node (new XMLNode (X_("PortEngine")));
|
||||||
|
for (auto const& port : _system_inputs) {
|
||||||
|
assert (port->is_physical () && port->is_terminal ());
|
||||||
|
const std::set<BackendPortPtr>& connected_ports = port->get_connections ();
|
||||||
|
for (auto const& other : connected_ports) {
|
||||||
|
if (!other->is_physical () || !other->is_terminal ()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
XMLNode* child = node->add_child (X_("HWConnection"));
|
||||||
|
child->set_property (X_("source"), port->name ());
|
||||||
|
child->set_property (X_("sink"), other->name ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto const& port : _system_midi_in) {
|
||||||
|
assert (port->is_physical () && port->is_terminal ());
|
||||||
|
const std::set<BackendPortPtr>& connected_ports = port->get_connections ();
|
||||||
|
for (auto const& other : connected_ports) {
|
||||||
|
if (!other->is_physical () || !other->is_terminal ()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
XMLNode* child = node->add_child (X_("HWConnection"));
|
||||||
|
child->set_property (X_("source"), port->name ());
|
||||||
|
child->set_property (X_("sink"), other->name ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PortEngineSharedImpl::set_state (XMLNode const & node, int)
|
||||||
|
{
|
||||||
|
assert (node.name() == X_("PortEngine"));
|
||||||
|
const XMLNodeList& children (node.children());
|
||||||
|
for (auto const* c : children) {
|
||||||
|
std::string src;
|
||||||
|
std::string dst;
|
||||||
|
if (c->name() != X_("HWConnection") || !c->get_property (X_("source"), src) || !c->get_property (X_("sink"), dst)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
connect (src, dst);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
void
|
void
|
||||||
PortEngineSharedImpl::list_ports () const
|
PortEngineSharedImpl::list_ports () const
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,7 @@ Session::Session (AudioEngine &eng,
|
||||||
, no_questions_about_missing_files (false)
|
, no_questions_about_missing_files (false)
|
||||||
, _bundles (new BundleList)
|
, _bundles (new BundleList)
|
||||||
, _bundle_xml_node (0)
|
, _bundle_xml_node (0)
|
||||||
|
, _engine_state (0)
|
||||||
, _clicking (false)
|
, _clicking (false)
|
||||||
, _click_rec_only (false)
|
, _click_rec_only (false)
|
||||||
, click_data (0)
|
, click_data (0)
|
||||||
|
|
@ -713,6 +714,8 @@ Session::destroy ()
|
||||||
|
|
||||||
/* clear state tree so that no references to objects are held any more */
|
/* clear state tree so that no references to objects are held any more */
|
||||||
|
|
||||||
|
delete _engine_state;
|
||||||
|
|
||||||
delete state_tree;
|
delete state_tree;
|
||||||
state_tree = 0;
|
state_tree = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1238,6 +1238,9 @@ Session::state (bool save_template, snapshot_t snapshot_type, bool for_archive,
|
||||||
node->set_property ("name", _name);
|
node->set_property ("name", _name);
|
||||||
node->set_property ("sample-rate", _base_sample_rate);
|
node->set_property ("sample-rate", _base_sample_rate);
|
||||||
|
|
||||||
|
if (!_engine_state) {
|
||||||
|
_engine_state = new XMLNode (X_("EngineState"));
|
||||||
|
}
|
||||||
/* store the last engine device we we can avoid autostarting on a different device with wrong i/o count */
|
/* store the last engine device we we can avoid autostarting on a different device with wrong i/o count */
|
||||||
std::shared_ptr<AudioBackend> backend = _engine.current_backend();
|
std::shared_ptr<AudioBackend> backend = _engine.current_backend();
|
||||||
if (!for_archive && _engine.running () && backend && _engine.setup_required ()) {
|
if (!for_archive && _engine.running () && backend && _engine.setup_required ()) {
|
||||||
|
|
@ -1250,7 +1253,21 @@ Session::state (bool save_template, snapshot_t snapshot_type, bool for_archive,
|
||||||
child->set_property ("input-device", backend->device_name ());
|
child->set_property ("input-device", backend->device_name ());
|
||||||
child->set_property ("output-device", backend->device_name ());
|
child->set_property ("output-device", backend->device_name ());
|
||||||
}
|
}
|
||||||
|
/* store port-engine external connections */
|
||||||
|
XMLNode* backend_state = backend->get_state();
|
||||||
|
if (backend_state) {
|
||||||
|
XMLNode* engine_state = new XMLNode (X_("EngineState"));
|
||||||
|
for (auto const& s : _engine_state->children ()) {
|
||||||
|
if (!backend->match_state (*s, CURRENT_SESSION_FILE_VERSION)) {
|
||||||
|
engine_state->add_child_copy (*s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
engine_state->add_child_nocopy (*backend_state);
|
||||||
|
delete _engine_state;
|
||||||
|
_engine_state = engine_state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
node->add_child_copy (*_engine_state);
|
||||||
|
|
||||||
if (session_dirs.size() > 1) {
|
if (session_dirs.size() > 1) {
|
||||||
|
|
||||||
|
|
@ -1846,6 +1863,16 @@ Session::set_state (const XMLNode& node, int version)
|
||||||
_midi_ports->set_midi_port_states (child->children());
|
_midi_ports->set_midi_port_states (child->children());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((child = find_named_node (node, "EngineState")) != 0) {
|
||||||
|
_engine_state = new XMLNode (*child);
|
||||||
|
if (Config->get_restore_hardware_connections ()) {
|
||||||
|
std::shared_ptr<AudioBackend> backend = _engine.current_backend();
|
||||||
|
for (auto const& s: _engine_state->children ()) {
|
||||||
|
backend->set_state (*s, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Stateful::save_extra_xml (node);
|
Stateful::save_extra_xml (node);
|
||||||
|
|
||||||
if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
|
if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
|
||||||
|
|
|
||||||
|
|
@ -1909,6 +1909,17 @@ Session::engine_running ()
|
||||||
{
|
{
|
||||||
_transport_fsm->start ();
|
_transport_fsm->start ();
|
||||||
reset_xrun_count ();
|
reset_xrun_count ();
|
||||||
|
|
||||||
|
if (_engine_state && Config->get_restore_hardware_connections ()) {
|
||||||
|
/* Note this restores the connections from the most recent [pending] save.
|
||||||
|
* Which may or may not be idendical to the ones used before the engine
|
||||||
|
* re-started.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<AudioBackend> backend = _engine.current_backend();
|
||||||
|
for (auto const& s: _engine_state->children ()) {
|
||||||
|
backend->set_state (*s, Stateful::loading_state_version);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -2435,3 +2435,47 @@ AlsaDeviceReservation::reservation_stdout (std::string d, size_t /* s */)
|
||||||
_reservation_succeeded = true;
|
_reservation_succeeded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ****************************************************************************/
|
||||||
|
|
||||||
|
XMLNode*
|
||||||
|
AlsaAudioBackend::get_state () const {
|
||||||
|
XMLNode* node = PortEngineSharedImpl::get_state ();
|
||||||
|
node->set_property ("backend", name ());
|
||||||
|
node->set_property ("driver", driver_name ());
|
||||||
|
node->set_property ("input-device", input_device_name ());
|
||||||
|
node->set_property ("output-device", output_device_name ());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
AlsaAudioBackend::set_state (XMLNode const& node, int version)
|
||||||
|
{
|
||||||
|
if (match_state (node, version)) {
|
||||||
|
return PortEngineSharedImpl::set_state (node, version);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AlsaAudioBackend::match_state (XMLNode const& node, int version)
|
||||||
|
{
|
||||||
|
if (node.name() != X_("PortEngine")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string val;
|
||||||
|
|
||||||
|
if (!node.get_property ("backend", val) || val != name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("driver", val) || val != driver_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("input-device", val) || val != input_device_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("output-device", val) || val != output_device_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,10 @@ class AlsaAudioBackend : public AudioBackend, public PortEngineSharedImpl
|
||||||
bool physically_connected (PortEngine::PortHandle ph, bool process_callback_safe) { return PortEngineSharedImpl::physically_connected (ph, process_callback_safe); }
|
bool physically_connected (PortEngine::PortHandle ph, bool process_callback_safe) { return PortEngineSharedImpl::physically_connected (ph, process_callback_safe); }
|
||||||
int get_connections (PortEngine::PortHandle ph, std::vector<std::string>& results, bool process_callback_safe) { return PortEngineSharedImpl::get_connections (ph, results, process_callback_safe); }
|
int get_connections (PortEngine::PortHandle ph, std::vector<std::string>& results, bool process_callback_safe) { return PortEngineSharedImpl::get_connections (ph, results, process_callback_safe); }
|
||||||
|
|
||||||
|
XMLNode* get_state () const;
|
||||||
|
int set_state (XMLNode const& node, int version);
|
||||||
|
bool match_state (XMLNode const&, int version);
|
||||||
|
|
||||||
/* MIDI */
|
/* MIDI */
|
||||||
int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index);
|
int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index);
|
||||||
int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
|
int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
|
||||||
|
|
|
||||||
|
|
@ -1916,3 +1916,48 @@ DummyMidiEvent::DummyMidiEvent (const DummyMidiEvent& other)
|
||||||
DummyMidiEvent::~DummyMidiEvent () {
|
DummyMidiEvent::~DummyMidiEvent () {
|
||||||
free (_data);
|
free (_data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************************************************************/
|
||||||
|
|
||||||
|
XMLNode*
|
||||||
|
DummyAudioBackend::get_state () const {
|
||||||
|
XMLNode* node = PortEngineSharedImpl::get_state ();
|
||||||
|
node->set_property ("backend", name ());
|
||||||
|
node->set_property ("driver", driver_name ());
|
||||||
|
node->set_property ("device", device_name ());
|
||||||
|
node->set_property ("instance", s_instance_name);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
DummyAudioBackend::set_state (XMLNode const& node, int version)
|
||||||
|
{
|
||||||
|
if (match_state (node, version)) {
|
||||||
|
return PortEngineSharedImpl::set_state (node, version);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
DummyAudioBackend::match_state (XMLNode const& node, int version)
|
||||||
|
{
|
||||||
|
if (node.name() != X_("PortEngine")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string val;
|
||||||
|
|
||||||
|
if (!node.get_property ("backend", val) || val != name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("driver", val) || val != driver_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("device", val) || val != device_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("instance", val) || val != s_instance_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,10 @@ class DummyAudioBackend : public AudioBackend, public PortEngineSharedImpl
|
||||||
bool physically_connected (PortEngine::PortHandle ph, bool process_callback_safe) { return PortEngineSharedImpl::physically_connected (ph, process_callback_safe); }
|
bool physically_connected (PortEngine::PortHandle ph, bool process_callback_safe) { return PortEngineSharedImpl::physically_connected (ph, process_callback_safe); }
|
||||||
int get_connections (PortEngine::PortHandle ph, std::vector<std::string>& results, bool process_callback_safe) { return PortEngineSharedImpl::get_connections (ph, results, process_callback_safe); }
|
int get_connections (PortEngine::PortHandle ph, std::vector<std::string>& results, bool process_callback_safe) { return PortEngineSharedImpl::get_connections (ph, results, process_callback_safe); }
|
||||||
|
|
||||||
|
XMLNode* get_state () const;
|
||||||
|
int set_state (XMLNode const&, int);
|
||||||
|
bool match_state (XMLNode const&, int version);
|
||||||
|
|
||||||
|
|
||||||
/* MIDI */
|
/* MIDI */
|
||||||
int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index);
|
int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index);
|
||||||
|
|
|
||||||
|
|
@ -1949,3 +1949,48 @@ PortMidiEvent::PortMidiEvent (const PortMidiEvent& other)
|
||||||
memcpy (_data, other._data, other._size);
|
memcpy (_data, other._data, other._size);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************************************************************/
|
||||||
|
|
||||||
|
XMLNode*
|
||||||
|
PortAudioBackend::get_state () const {
|
||||||
|
XMLNode& node = PortEngineSharedImpl::get_state ();
|
||||||
|
node.set_property ("backend", name ());
|
||||||
|
node.set_property ("driver", driver_name ());
|
||||||
|
node.set_property ("input-device", input_device_name ());
|
||||||
|
node.set_property ("output-device", output_device_name ());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PortAudioBackend::set_state (XMLNode const& node, int version)
|
||||||
|
{
|
||||||
|
if (match_state (node, version)) {
|
||||||
|
return PortEngineSharedImpl::set_state (node, version);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PortAudioBackend::match_state (XMLNode const& node, int version)
|
||||||
|
{
|
||||||
|
if (node.name() != X_("PortEngine")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string val;
|
||||||
|
|
||||||
|
if (!node.get_property ("backend", val) || val != name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("driver", val) || val != driver_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("input-device", val) || val != input_device_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!node.get_property ("output-device", val) || val != output_device_name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,10 @@ class PortAudioBackend : public AudioBackend, public PortEngineSharedImpl {
|
||||||
bool physically_connected (PortEngine::PortHandle ph, bool process_callback_safe) { return PortEngineSharedImpl::physically_connected (ph, process_callback_safe); }
|
bool physically_connected (PortEngine::PortHandle ph, bool process_callback_safe) { return PortEngineSharedImpl::physically_connected (ph, process_callback_safe); }
|
||||||
int get_connections (PortEngine::PortHandle ph, std::vector<std::string>& results, bool process_callback_safe) { return PortEngineSharedImpl::get_connections (ph, results, process_callback_safe); }
|
int get_connections (PortEngine::PortHandle ph, std::vector<std::string>& results, bool process_callback_safe) { return PortEngineSharedImpl::get_connections (ph, results, process_callback_safe); }
|
||||||
|
|
||||||
|
XMLNode* get_state () const;
|
||||||
|
int set_state (XMLNode const& node, int version);
|
||||||
|
bool match_state (XMLNode const&, int version);
|
||||||
|
|
||||||
/* MIDI */
|
/* MIDI */
|
||||||
int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index);
|
int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index);
|
||||||
int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
|
int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue