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;
|
||||
|
||||
/* 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:
|
||||
PortManager& manager;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -245,6 +245,9 @@ protected:
|
|||
|
||||
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
|
||||
void list_ports () const;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ CONFIG_VARIABLE (Temporal::TimeDomain, preferred_time_domain, "preferred_time_do
|
|||
|
||||
/* IO connection */
|
||||
|
||||
CONFIG_VARIABLE (bool, restore_hardware_connections, "restore-hardware-connections", true)
|
||||
CONFIG_VARIABLE (bool, auto_connect_standard_busses, "auto-connect-standard-busses", true)
|
||||
/* this variable is used to indicate output mode in Waves Tracks:
|
||||
"Multi Out" == AutoConnectPhysical and "Stereo Out" == AutoConnectMaster
|
||||
|
|
|
|||
|
|
@ -2084,6 +2084,8 @@ private:
|
|||
XMLNode* _bundle_xml_node;
|
||||
int load_bundles (XMLNode const &);
|
||||
|
||||
mutable XMLNode* _engine_state;
|
||||
|
||||
int backend_sync_callback (TransportState, samplepos_t);
|
||||
|
||||
void process_rtop (SessionEvent*);
|
||||
|
|
|
|||
|
|
@ -839,6 +839,54 @@ PortEngineSharedImpl::process_connection_queue_locked (PortManager& mgr)
|
|||
_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
|
||||
void
|
||||
PortEngineSharedImpl::list_ports () const
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ Session::Session (AudioEngine &eng,
|
|||
, no_questions_about_missing_files (false)
|
||||
, _bundles (new BundleList)
|
||||
, _bundle_xml_node (0)
|
||||
, _engine_state (0)
|
||||
, _clicking (false)
|
||||
, _click_rec_only (false)
|
||||
, click_data (0)
|
||||
|
|
@ -713,6 +714,8 @@ Session::destroy ()
|
|||
|
||||
/* clear state tree so that no references to objects are held any more */
|
||||
|
||||
delete _engine_state;
|
||||
|
||||
delete state_tree;
|
||||
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 ("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 */
|
||||
std::shared_ptr<AudioBackend> backend = _engine.current_backend();
|
||||
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 ("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) {
|
||||
|
||||
|
|
@ -1846,6 +1863,16 @@ Session::set_state (const XMLNode& node, int version)
|
|||
_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);
|
||||
|
||||
if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
|
||||
|
|
|
|||
|
|
@ -1909,6 +1909,17 @@ Session::engine_running ()
|
|||
{
|
||||
_transport_fsm->start ();
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2435,3 +2435,47 @@ AlsaDeviceReservation::reservation_stdout (std::string d, size_t /* s */)
|
|||
_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); }
|
||||
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 */
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -1916,3 +1916,48 @@ DummyMidiEvent::DummyMidiEvent (const DummyMidiEvent& other)
|
|||
DummyMidiEvent::~DummyMidiEvent () {
|
||||
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); }
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
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); }
|
||||
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 */
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue