From b2cf028fcba055110a9b1bf36af0fb6bd443c1af Mon Sep 17 00:00:00 2001 From: Tim Mayberry Date: Fri, 4 Dec 2015 22:23:01 +1000 Subject: [PATCH] Implement MIDI device enumeration and latency offset/calibration in portaudio backend --- libs/backends/portaudio/midi_device_info.h | 20 +++ libs/backends/portaudio/portaudio_backend.cc | 102 ++++++++++++++++ libs/backends/portaudio/portaudio_backend.h | 24 ++-- libs/backends/portaudio/winmmemidi_io.cc | 121 ++++++++++++++++++- libs/backends/portaudio/winmmemidi_io.h | 16 +++ 5 files changed, 267 insertions(+), 16 deletions(-) create mode 100644 libs/backends/portaudio/midi_device_info.h diff --git a/libs/backends/portaudio/midi_device_info.h b/libs/backends/portaudio/midi_device_info.h new file mode 100644 index 0000000000..7b470df891 --- /dev/null +++ b/libs/backends/portaudio/midi_device_info.h @@ -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 diff --git a/libs/backends/portaudio/portaudio_backend.cc b/libs/backends/portaudio/portaudio_backend.cc index 4f74992adf..0f816c991f 100644 --- a/libs/backends/portaudio/portaudio_backend.cc +++ b/libs/backends/portaudio/portaudio_backend.cc @@ -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 +PortAudioBackend::enumerate_midi_devices () const +{ + std::vector midi_device_status; + std::vector device_info; + + if (_midi_driver_option == winmme_driver_name) { + _midiio->update_device_info (); + device_info = _midiio->get_device_info (); + } + + for (std::vector::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 dev_info; + + if (_midi_driver_option == winmme_driver_name) { + dev_info = _midiio->get_device_info(); + + for (std::vector::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(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(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(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(p); midi_port->set_n_periods(2); midi_port->set_pretty_name ((*i)->name()); diff --git a/libs/backends/portaudio/portaudio_backend.h b/libs/backends/portaudio/portaudio_backend.h index f0ef5fa993..b028dc212b 100644 --- a/libs/backends/portaudio/portaudio_backend.h +++ b/libs/backends/portaudio/portaudio_backend.h @@ -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 enumerate_midi_devices () const { - return std::vector (); - } - int set_midi_device_enabled (std::string const, bool) { - return 0; - } - bool midi_device_enabled (std::string const) const { - return true; - } + std::vector 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; diff --git a/libs/backends/portaudio/winmmemidi_io.cc b/libs/backends/portaudio/winmmemidi_io.cc index 427ff486d7..d845e60551 100644 --- a/libs/backends/portaudio/winmmemidi_io.cc +++ b/libs/backends/portaudio/winmmemidi_io.cc @@ -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::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 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::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::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) { diff --git a/libs/backends/portaudio/winmmemidi_io.h b/libs/backends/portaudio/winmmemidi_io.h index 28450e95cb..672a68cc73 100644 --- a/libs/backends/portaudio/winmmemidi_io.h +++ b/libs/backends/portaudio/winmmemidi_io.h @@ -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 get_inputs () { return m_inputs; } std::vector get_outputs () { return m_outputs; } + void update_device_info (); + + std::vector 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 m_device_info; + std::vector m_inputs; std::vector m_outputs; -- 2.30.2