ALSA: allow to dynamically add/remove midi devices & update their latency.
authorRobin Gareus <robin@gareus.org>
Fri, 4 Dec 2015 21:19:47 +0000 (22:19 +0100)
committerRobin Gareus <robin@gareus.org>
Fri, 4 Dec 2015 21:19:47 +0000 (22:19 +0100)
libs/backends/alsa/alsa_audiobackend.cc
libs/backends/alsa/alsa_audiobackend.h
libs/backends/alsa/alsa_midi.h
libs/backends/alsa/alsa_rawmidi.cc
libs/backends/alsa/alsa_rawmidi.h
libs/backends/alsa/alsa_sequencer.cc
libs/backends/alsa/alsa_sequencer.h

index 25a5e3f12775860eae1c94eece7123d4fa708d04..8a25f548d73f1df8d29d15506a202ab0f378f5e7 100644 (file)
@@ -70,6 +70,8 @@ AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info)
        , _dsp_load (0)
        , _processed_samples (0)
        , _port_change_flag (false)
+       , _midi_ins (0)
+       , _midi_outs (0)
 {
        _instance_name = s_instance_name;
        pthread_mutex_init (&_port_callback_mutex, 0);
@@ -544,19 +546,27 @@ AlsaAudioBackend::update_systemic_audio_latencies ()
 void
 AlsaAudioBackend::update_systemic_midi_latencies ()
 {
-#if 0
-       // TODO, need a way to associate the port with the corresponding AlsaMidiDeviceInfo
-       for (std::vector<AlsaPort*>::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it) {
+       uint32_t i = 0;
+       for (std::vector<AlsaPort*>::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
+               assert (_rmidi_out.size() > i);
+               AlsaMidiOut *rm = _rmidi_out.at(i);
+               struct AlsaMidiDeviceInfo * nfo = midi_device_info (rm->name());
+               assert (nfo);
                LatencyRange lr;
                lr.min = lr.max = (_measure_latency ? 0 : nfo->systemic_output_latency);
                set_latency_range (*it, false, lr);
        }
-       for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it) {
+
+       i = 0;
+       for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) {
+               assert (_rmidi_in.size() > i);
+               AlsaMidiIO *rm = _rmidi_in.at(i);
+               struct AlsaMidiDeviceInfo * nfo = midi_device_info (rm->name());
+               assert (nfo);
                LatencyRange lr;
                lr.min = lr.max = (_measure_latency ? 0 : nfo->systemic_input_latency);
                set_latency_range (*it, true, lr);
        }
-#endif
        update_latencies ();
 }
 
@@ -712,6 +722,9 @@ AlsaAudioBackend::set_midi_option (const std::string& opt)
        if (opt != get_standard_device_name(DeviceNone) && opt != _("ALSA raw devices") && opt != _("ALSA sequencer")) {
                return -1;
        }
+       if (_run && _midi_driver_option != opt) {
+               return -1;
+       }
        _midi_driver_option = opt;
        return 0;
 }
@@ -727,8 +740,41 @@ AlsaAudioBackend::set_midi_device_enabled (std::string const device, bool enable
 {
        struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
        if (!nfo) return -1;
+       const bool prev_enabled = nfo->enabled;
        nfo->enabled = enable;
-       // TODO - trigger update when already running
+
+       if (_run && prev_enabled != enable) {
+               if (enable) {
+                       // add ports for the given device
+                       register_system_midi_ports(device);
+               } else {
+                       // remove all ports provided by the given device
+                       uint32_t i = 0;
+                       for (std::vector<AlsaPort*>::iterator it = _system_midi_out.begin (); it != _system_midi_out.end ();) {
+                               assert (_rmidi_out.size() > i);
+                               AlsaMidiOut *rm = _rmidi_out.at(i);
+                               if (rm->name () != device) { ++it; ++i; continue; }
+                               it = _system_midi_out.erase (it);
+                               unregister_port (*it);
+                               rm->stop();
+                               _rmidi_out.erase (_rmidi_out.begin() + i);
+                               delete rm;
+                       }
+
+                       i = 0;
+                       for (std::vector<AlsaPort*>::iterator it = _system_midi_in.begin (); it != _system_midi_in.end ();) {
+                               assert (_rmidi_in.size() > i);
+                               AlsaMidiIn *rm = _rmidi_in.at(i);
+                               if (rm->name () != device) { ++it; ++i; continue; }
+                               it = _system_midi_in.erase (it);
+                               unregister_port (*it);
+                               rm->stop();
+                               _rmidi_in.erase (_rmidi_in.begin() + i);
+                               delete rm;
+                       }
+               }
+               update_systemic_midi_latencies ();
+       }
        return 0;
 }
 
@@ -909,6 +955,7 @@ AlsaAudioBackend::_start (bool for_latency_measurement)
 
        _measure_latency = for_latency_measurement;
 
+       _midi_ins = _midi_outs = 0;
        register_system_midi_ports();
 
        if (register_system_audio_ports()) {
@@ -990,6 +1037,7 @@ AlsaAudioBackend::stop ()
 
        unregister_ports();
        delete _pcmi; _pcmi = 0;
+       _midi_ins = _midi_outs = 0;
        release_device();
 
        return (_active == false) ? 0 : -1;
@@ -1312,11 +1360,11 @@ AlsaAudioBackend::register_system_audio_ports()
 }
 
 int
-AlsaAudioBackend::register_system_midi_ports()
+AlsaAudioBackend::register_system_midi_ports(const std::string device)
 {
        std::map<std::string, std::string> devices;
-       int midi_ins = 0;
-       int midi_outs = 0;
+
+       // TODO use consistent numbering when re-adding devices: _midi_ins, _midi_outs
 
        if (_midi_driver_option == get_standard_device_name(DeviceNone)) {
                return 0;
@@ -1327,15 +1375,18 @@ AlsaAudioBackend::register_system_midi_ports()
        }
 
        for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+               if (!device.empty() && device != i->first) {
+                       continue;
+               }
                struct AlsaMidiDeviceInfo * nfo = midi_device_info(i->first);
                if (!nfo) continue;
                if (!nfo->enabled) continue;
 
                AlsaMidiOut *mout;
                if (_midi_driver_option == _("ALSA raw devices")) {
-                       mout = new AlsaRawMidiOut (i->second.c_str());
+                       mout = new AlsaRawMidiOut (i->first, i->second.c_str());
                } else {
-                       mout = new AlsaSeqMidiOut (i->second.c_str());
+                       mout = new AlsaSeqMidiOut (i->first, i->second.c_str());
                }
 
                if (mout->state ()) {
@@ -1353,7 +1404,7 @@ AlsaAudioBackend::register_system_midi_ports()
                                delete mout;
                        } else {
                                char tmp[64];
-                               snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++midi_ins);
+                               snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++_midi_ins);
                                PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
                                if (!p) {
                                        mout->stop();
@@ -1370,9 +1421,9 @@ AlsaAudioBackend::register_system_midi_ports()
 
                AlsaMidiIn *midin;
                if (_midi_driver_option == _("ALSA raw devices")) {
-                       midin = new AlsaRawMidiIn (i->second.c_str());
+                       midin = new AlsaRawMidiIn (i->first, i->second.c_str());
                } else {
-                       midin = new AlsaSeqMidiIn (i->second.c_str());
+                       midin = new AlsaSeqMidiIn (i->first, i->second.c_str());
                }
 
                if (midin->state ()) {
@@ -1390,7 +1441,7 @@ AlsaAudioBackend::register_system_midi_ports()
                                delete midin;
                        } else {
                                char tmp[64];
-                               snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++midi_outs);
+                               snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++_midi_outs);
                                PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
                                if (!p) {
                                        midin->stop();
index a661aa2979faaa26ec240a5f450c7a09dd60a029..2233c9be9e16098b19b38c6bfa460aa099ab7c9c 100644 (file)
@@ -395,7 +395,7 @@ class AlsaAudioBackend : public AudioBackend {
                /* port engine */
                PortHandle add_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags);
                int register_system_audio_ports ();
-               int register_system_midi_ports ();
+               int register_system_midi_ports (const std::string device = "");
                void unregister_ports (bool system_only = false);
 
                std::vector<AlsaPort *> _ports;
@@ -407,6 +407,9 @@ class AlsaAudioBackend : public AudioBackend {
                std::vector<AlsaMidiOut *> _rmidi_out;
                std::vector<AlsaMidiIn  *> _rmidi_in;
 
+               unsigned _midi_ins;
+               unsigned _midi_outs;
+
                struct PortConnectData {
                        std::string a;
                        std::string b;
index 7da991d66e5a812b8348cafd7a50d4dc050d912f..254835591fc57b257dc63bd31cb4baeba7cf33b7 100644 (file)
@@ -42,6 +42,8 @@ public:
 
        virtual void* main_process_thread () = 0;
 
+       const std::string & name () const { return _name; }
+
 protected:
        pthread_t _main_thread;
        pthread_mutex_t _notify_mutex;
@@ -68,7 +70,8 @@ protected:
 
        RingBuffer<uint8_t>* _rb;
 
-protected:
+       std::string _name;
+
        virtual void init (const char *device_name, const bool input) = 0;
 
 };
index e4678ba26906a13eda1efc4a284fe33f41bbd667..303649b9e70bcf1e6ec5c2f12439062f83d7b68d 100644 (file)
@@ -38,10 +38,11 @@ using namespace ARDOUR;
 #define _DEBUGPRINT(STR) ;
 #endif
 
-AlsaRawMidiIO::AlsaRawMidiIO (const char *device, const bool input)
+AlsaRawMidiIO::AlsaRawMidiIO (const std::string &name, const char *device, const bool input)
        : AlsaMidiIO()
        , _device (0)
 {
+       _name = name;
        init (device, input);
 }
 
@@ -107,8 +108,8 @@ initerr:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-AlsaRawMidiOut::AlsaRawMidiOut (const char *device)
-               : AlsaRawMidiIO (device, false)
+AlsaRawMidiOut::AlsaRawMidiOut (const std::string &name, const char *device)
+               : AlsaRawMidiIO (name, device, false)
                , AlsaMidiOut ()
 {
 }
@@ -223,8 +224,8 @@ retry:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-AlsaRawMidiIn::AlsaRawMidiIn (const char *device)
-               : AlsaRawMidiIO (device, true)
+AlsaRawMidiIn::AlsaRawMidiIn (const std::string &name, const char *device)
+               : AlsaRawMidiIO (name, device, true)
                , AlsaMidiIn ()
                , _event(0,0)
                , _first_time(true)
index 5d9a86c8dbd9acbd80c7c8621b1225d5113faf32..654f084c4e465d31a73a54bdcf7818d12c954dc1 100644 (file)
@@ -33,7 +33,7 @@ namespace ARDOUR {
 
 class AlsaRawMidiIO : virtual public AlsaMidiIO {
 public:
-       AlsaRawMidiIO (const char *device, const bool input);
+       AlsaRawMidiIO (const std::string &name, const char *device, const bool input);
        virtual ~AlsaRawMidiIO ();
 
 protected:
@@ -46,14 +46,14 @@ private:
 class AlsaRawMidiOut : public AlsaRawMidiIO, public AlsaMidiOut
 {
 public:
-       AlsaRawMidiOut (const char *device);
+       AlsaRawMidiOut (const std::string &name, const char *device);
        void* main_process_thread ();
 };
 
 class AlsaRawMidiIn : public AlsaRawMidiIO, public AlsaMidiIn
 {
 public:
-       AlsaRawMidiIn (const char *device);
+       AlsaRawMidiIn (const std::string &name, const char *device);
 
        void* main_process_thread ();
 
index aa0aac09fa9f7ee7186d7e3c6ce014cf7868bd82..cb77c3c48dc35022b716099c4279dd810cce579e 100644 (file)
@@ -37,10 +37,11 @@ using namespace ARDOUR;
 #define _DEBUGPRINT(STR) ;
 #endif
 
-AlsaSeqMidiIO::AlsaSeqMidiIO (const char *device, const bool input)
+AlsaSeqMidiIO::AlsaSeqMidiIO (const std::string &name, const char *device, const bool input)
        : AlsaMidiIO()
        , _seq (0)
 {
+       _name = name;
        init (device, input);
 }
 
@@ -117,8 +118,8 @@ initerr:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-AlsaSeqMidiOut::AlsaSeqMidiOut (const char *device)
-               : AlsaSeqMidiIO (device, false)
+AlsaSeqMidiOut::AlsaSeqMidiOut (const std::string &name, const char *device)
+               : AlsaSeqMidiIO (name, device, false)
                , AlsaMidiOut ()
 {
 }
@@ -227,8 +228,8 @@ retry:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-AlsaSeqMidiIn::AlsaSeqMidiIn (const char *device)
-               : AlsaSeqMidiIO (device, true)
+AlsaSeqMidiIn::AlsaSeqMidiIn (const std::string &name, const char *device)
+               : AlsaSeqMidiIO (name, device, true)
                , AlsaMidiIn ()
 {
 }
index bc00751acfdcaa2f687d316b53d9b643987ef56d..2c27778c97b5e02b2141d7f8c08172f2bb4308ac 100644 (file)
@@ -33,7 +33,7 @@ namespace ARDOUR {
 
 class AlsaSeqMidiIO : virtual public AlsaMidiIO {
 public:
-       AlsaSeqMidiIO (const char *port_name, const bool input);
+       AlsaSeqMidiIO (const std::string &name, const char *port_name, const bool input);
        virtual ~AlsaSeqMidiIO ();
 
 protected:
@@ -47,14 +47,14 @@ private:
 class AlsaSeqMidiOut : public AlsaSeqMidiIO, public AlsaMidiOut
 {
 public:
-       AlsaSeqMidiOut (const char *port_name);
+       AlsaSeqMidiOut (const std::string &name, const char *port_name);
        void* main_process_thread ();
 };
 
 class AlsaSeqMidiIn : public AlsaSeqMidiIO, public AlsaMidiIn
 {
 public:
-       AlsaSeqMidiIn (const char *port_name);
+       AlsaSeqMidiIn (const std::string &name, const char *port_name);
 
        void* main_process_thread ();
 };