Implement MIDI device enumeration and latency offset/calibration in portaudio backend
authorTim Mayberry <mojofunk@gmail.com>
Fri, 4 Dec 2015 12:23:01 +0000 (22:23 +1000)
committerTim Mayberry <mojofunk@gmail.com>
Thu, 11 Feb 2016 02:15:07 +0000 (12:15 +1000)
libs/backends/portaudio/midi_device_info.h [new file with mode: 0644]
libs/backends/portaudio/portaudio_backend.cc
libs/backends/portaudio/portaudio_backend.h
libs/backends/portaudio/winmmemidi_io.cc
libs/backends/portaudio/winmmemidi_io.h

diff --git a/libs/backends/portaudio/midi_device_info.h b/libs/backends/portaudio/midi_device_info.h
new file mode 100644 (file)
index 0000000..7b470df
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef MIDI_DEVICE_INFO_H
+#define MIDI_DEVICE_INFO_H
+
+/* midi settings */
+struct MidiDeviceInfo {
+       MidiDeviceInfo(const std::string& dev_name)
+           : device_name(dev_name)
+           , enable(true)
+           , systemic_input_latency(0)
+           , systemic_output_latency(0)
+       {
+       }
+
+       std::string device_name;
+       bool enable;
+       uint32_t systemic_input_latency;
+       uint32_t systemic_output_latency;
+};
+
+#endif // MIDI_DEVICE_INFO_H
index 4f74992adf0f974eee583ca5a718049b1c4807f7..0f816c991fb9f4814a5e1061c5507378dd08668c 100644 (file)
@@ -156,6 +156,7 @@ PortAudioBackend::set_driver (const std::string& name)
 bool
 PortAudioBackend::update_devices ()
 {
+       // update midi device info?
        return _pcmio->update_devices();
 }
 
@@ -329,6 +330,24 @@ PortAudioBackend::set_systemic_output_latency (uint32_t sl)
        return 0;
 }
 
+int
+PortAudioBackend::set_systemic_midi_input_latency (std::string const device, uint32_t sl)
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return -1;
+       nfo->systemic_input_latency = sl;
+       return 0;
+}
+
+int
+PortAudioBackend::set_systemic_midi_output_latency (std::string const device, uint32_t sl)
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return -1;
+       nfo->systemic_output_latency = sl;
+       return 0;
+}
+
 /* Retrieving parameters */
 std::string
 PortAudioBackend::device_name () const
@@ -390,6 +409,22 @@ PortAudioBackend::systemic_output_latency () const
        return _systemic_audio_output_latency;
 }
 
+uint32_t
+PortAudioBackend::systemic_midi_input_latency (std::string const device) const
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return 0;
+       return nfo->systemic_input_latency;
+}
+
+uint32_t
+PortAudioBackend::systemic_midi_output_latency (std::string const device) const
+{
+       MidiDeviceInfo* nfo = midi_device_info (device);
+       if (!nfo) return 0;
+       return nfo->systemic_output_latency;
+}
+
 std::string
 PortAudioBackend::control_app_name () const
 {
@@ -431,6 +466,61 @@ PortAudioBackend::midi_option () const
        return _midi_driver_option;
 }
 
+std::vector<AudioBackend::DeviceStatus>
+PortAudioBackend::enumerate_midi_devices () const
+{
+       std::vector<AudioBackend::DeviceStatus> midi_device_status;
+       std::vector<MidiDeviceInfo*> device_info;
+
+       if (_midi_driver_option == winmme_driver_name) {
+               _midiio->update_device_info ();
+               device_info = _midiio->get_device_info ();
+       }
+
+       for (std::vector<MidiDeviceInfo*>::const_iterator i = device_info.begin();
+            i != device_info.end();
+            ++i) {
+               midi_device_status.push_back(DeviceStatus((*i)->device_name, true));
+       }
+       return midi_device_status;
+}
+
+MidiDeviceInfo*
+PortAudioBackend::midi_device_info (const std::string& device_name) const
+{
+       std::vector<MidiDeviceInfo*> dev_info;
+
+       if (_midi_driver_option == winmme_driver_name) {
+               dev_info = _midiio->get_device_info();
+
+               for (std::vector<MidiDeviceInfo*>::const_iterator i = dev_info.begin();
+                    i != dev_info.end();
+                    ++i) {
+                       if ((*i)->device_name == device_name) {
+                               return *i;
+                       }
+               }
+       }
+       return 0;
+}
+
+int
+PortAudioBackend::set_midi_device_enabled (std::string const device, bool enable)
+{
+       MidiDeviceInfo* nfo = midi_device_info(device);
+       if (!nfo) return -1;
+       nfo->enable = enable;
+       return 0;
+}
+
+bool
+PortAudioBackend::midi_device_enabled (std::string const device) const
+{
+       MidiDeviceInfo* nfo = midi_device_info(device);
+       if (!nfo) return false;
+       return nfo->enable;
+}
+
 /* State Control */
 
 static void * blocking_thread_func (void *arg)
@@ -1309,7 +1399,13 @@ PortAudioBackend::register_system_midi_ports()
                              DataType::MIDI,
                              static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
                if (!p) return -1;
+
+               MidiDeviceInfo* info = _midiio->get_device_info((*i)->name());
+               if (info) { // assert?
+                       lr.min = lr.max = _samples_per_period + info->systemic_input_latency;
+               }
                set_latency_range (p, false, lr);
+
                PortMidiPort* midi_port = static_cast<PortMidiPort*>(p);
                midi_port->set_pretty_name ((*i)->name());
                _system_midi_in.push_back (midi_port);
@@ -1327,7 +1423,13 @@ PortAudioBackend::register_system_midi_ports()
                              DataType::MIDI,
                              static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
                if (!p) return -1;
+
+               MidiDeviceInfo* info = _midiio->get_device_info((*i)->name());
+               if (info) { // assert?
+                       lr.min = lr.max = _samples_per_period + info->systemic_output_latency;
+               }
                set_latency_range (p, false, lr);
+
                PortMidiPort* midi_port = static_cast<PortMidiPort*>(p);
                midi_port->set_n_periods(2);
                midi_port->set_pretty_name ((*i)->name());
index f0ef5fa9932df2a22b0d659176e3aba0e8d374e2..b028dc212bd5265fa8cfd782eff2fa7ad9502292 100644 (file)
@@ -197,8 +197,8 @@ class PortAudioBackend : public AudioBackend {
                int set_output_channels (uint32_t);
                int set_systemic_input_latency (uint32_t);
                int set_systemic_output_latency (uint32_t);
-               int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; }
-               int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; }
+               int set_systemic_midi_input_latency (std::string const, uint32_t);
+               int set_systemic_midi_output_latency (std::string const, uint32_t);
 
                int reset_device () { return 0; };
 
@@ -213,10 +213,10 @@ class PortAudioBackend : public AudioBackend {
                uint32_t     output_channels () const;
                uint32_t     systemic_input_latency () const;
                uint32_t     systemic_output_latency () const;
-               uint32_t     systemic_midi_input_latency (std::string const) const { return 0; }
-               uint32_t     systemic_midi_output_latency (std::string const) const { return 0; }
+               uint32_t     systemic_midi_input_latency (std::string const) const;
+               uint32_t     systemic_midi_output_latency (std::string const) const;
 
-               bool can_set_systemic_midi_latencies () const { return false; }
+               bool can_set_systemic_midi_latencies () const { return true; }
 
                /* External control app */
                std::string control_app_name () const;
@@ -227,15 +227,9 @@ class PortAudioBackend : public AudioBackend {
                int set_midi_option (const std::string&);
                std::string midi_option () const;
 
-               std::vector<DeviceStatus> enumerate_midi_devices () const {
-                       return std::vector<AudioBackend::DeviceStatus> ();
-               }
-               int set_midi_device_enabled (std::string const, bool) {
-                       return 0;
-               }
-               bool midi_device_enabled (std::string const) const {
-                       return true;
-               }
+               std::vector<DeviceStatus> enumerate_midi_devices () const;
+               int set_midi_device_enabled (std::string const, bool);
+               bool midi_device_enabled (std::string const) const;
 
        protected:
                /* State Control */
@@ -402,6 +396,8 @@ class PortAudioBackend : public AudioBackend {
                uint32_t _systemic_audio_input_latency;
                uint32_t _systemic_audio_output_latency;
 
+               MidiDeviceInfo* midi_device_info(const std::string&) const;
+
                /* portaudio specific  */
                int name_to_id(std::string) const;
 
index 427ff486d758fec7bfe78590977606df19fe5048..d845e60551492086eca793117861ae9da28c9cf6 100644 (file)
@@ -109,8 +109,7 @@ WinMMEMidiIO::port_id (uint32_t port, bool input)
        return ss.str();
 }
 
-std::string
-WinMMEMidiIO::port_name (uint32_t port, bool input)
+std::string WinMMEMidiIO::port_name(uint32_t port, bool input)
 {
        if (input) {
                if (port < m_inputs.size ()) {
@@ -200,6 +199,86 @@ WinMMEMidiIO::stop_devices ()
        }
 }
 
+void
+WinMMEMidiIO::clear_device_info ()
+{
+       for (std::vector<MidiDeviceInfo*>::iterator i = m_device_info.begin();
+            i != m_device_info.end();
+            ++i) {
+         delete *i;
+       }
+       m_device_info.clear();
+}
+
+bool
+WinMMEMidiIO::get_input_name_from_index (int index, std::string& name)
+{
+       MIDIINCAPS capabilities;
+       MMRESULT result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities));
+       if (result == MMSYSERR_NOERROR) {
+               name = capabilities.szPname;
+               return true;
+       }
+       return false;
+}
+
+bool
+WinMMEMidiIO::get_output_name_from_index (int index, std::string& name)
+{
+       MIDIOUTCAPS capabilities;
+       MMRESULT result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities));
+       if (result == MMSYSERR_NOERROR) {
+               name = capabilities.szPname;
+               return true;
+       }
+       return false;
+}
+
+void
+WinMMEMidiIO::update_device_info ()
+{
+       std::set<std::string> device_names;
+
+       int in_count = midiInGetNumDevs ();
+
+       for (int i = 0; i < in_count; ++i) {
+               std::string input_name;
+               if (get_input_name_from_index(i, input_name)) {
+                       device_names.insert(input_name);
+               }
+       }
+
+       int out_count = midiOutGetNumDevs ();
+
+       for (int i = 0; i < out_count; ++i) {
+               std::string output_name;
+               if (get_output_name_from_index(i, output_name)) {
+                       device_names.insert(output_name);
+               }
+       }
+
+       clear_device_info ();
+
+       for (std::set<std::string>::const_iterator i = device_names.begin();
+            i != device_names.end();
+            ++i) {
+         m_device_info.push_back(new MidiDeviceInfo(*i));
+       }
+}
+
+MidiDeviceInfo*
+WinMMEMidiIO::get_device_info (const std::string& name)
+{
+       for (std::vector<MidiDeviceInfo*>::const_iterator i = m_device_info.begin();
+            i != m_device_info.end();
+            ++i) {
+               if ((*i)->device_name == name) {
+                       return *i;
+               }
+       }
+       return 0;
+}
+
 void
 WinMMEMidiIO::create_input_devices ()
 {
@@ -208,6 +287,25 @@ WinMMEMidiIO::create_input_devices ()
        DEBUG_MIDI (string_compose ("MidiIn count: %1\n", srcCount));
 
        for (int i = 0; i < srcCount; ++i) {
+               std::string input_name;
+               if (!get_input_name_from_index (i, input_name)) {
+                       DEBUG_MIDI ("Unable to get MIDI input name from index\n");
+                       continue;
+               }
+
+               MidiDeviceInfo* info = get_device_info (input_name);
+
+               if (!info) {
+                       DEBUG_MIDI ("Unable to MIDI device info from name\n");
+                       continue;
+               }
+
+               if (!info->enable) {
+                       DEBUG_MIDI(string_compose(
+                           "MIDI input device %1 not enabled, not opening device\n", input_name));
+                       continue;
+               }
+
                try {
                        WinMMEMidiInputDevice* midi_input = new WinMMEMidiInputDevice (i);
                        if (midi_input) {
@@ -228,6 +326,25 @@ WinMMEMidiIO::create_output_devices ()
        DEBUG_MIDI (string_compose ("MidiOut count: %1\n", dstCount));
 
        for (int i = 0; i < dstCount; ++i) {
+               std::string output_name;
+               if (!get_output_name_from_index (i, output_name)) {
+                       DEBUG_MIDI ("Unable to get MIDI output name from index\n");
+                       continue;
+               }
+
+               MidiDeviceInfo* info = get_device_info (output_name);
+
+               if (!info) {
+                       DEBUG_MIDI ("Unable to MIDI device info from name\n");
+                       continue;
+               }
+
+               if (!info->enable) {
+                       DEBUG_MIDI(string_compose(
+                           "MIDI output device %1 not enabled, not opening device\n", output_name));
+                       continue;
+               }
+
                try {
                        WinMMEMidiOutputDevice* midi_output = new WinMMEMidiOutputDevice(i);
                        if (midi_output) {
index 28450e95cbe577b41e8b96fa6df3155855eaa810..672a68cc733ca78f2284d273fc2059370202e302 100644 (file)
@@ -31,6 +31,8 @@
 #include "winmmemidi_input_device.h"
 #include "winmmemidi_output_device.h"
 
+#include "midi_device_info.h"
+
 namespace ARDOUR {
 
 struct WinMMEMIDIPacket {
@@ -79,6 +81,12 @@ public:
        std::vector<WinMMEMidiInputDevice*> get_inputs () { return m_inputs; }
        std::vector<WinMMEMidiOutputDevice*> get_outputs () { return m_outputs; }
 
+       void update_device_info ();
+
+       std::vector<MidiDeviceInfo*> get_device_info () { return m_device_info; }
+
+       MidiDeviceInfo* get_device_info (const std::string& name);
+
        std::string port_id (uint32_t, bool input);
        std::string port_name (uint32_t, bool input);
 
@@ -91,6 +99,12 @@ public:
        }
 
 private: // Methods
+
+       void clear_device_info ();
+
+       static bool get_input_name_from_index (int index, std::string& name);
+       static bool get_output_name_from_index (int index, std::string& name);
+
        void discover ();
        void cleanup ();
 
@@ -105,6 +119,8 @@ private: // Methods
 
 private: // Data
 
+       std::vector<MidiDeviceInfo*> m_device_info;
+
        std::vector<WinMMEMidiInputDevice*> m_inputs;
        std::vector<WinMMEMidiOutputDevice*> m_outputs;