Skip to content

Commit 9123385

Browse files
committed
port information: refactor, add various possible implementations of lookup and a heuristics-based find_closest_port function
1 parent 84ea68f commit 9123385

8 files changed

Lines changed: 526 additions & 128 deletions

File tree

cmake/libremidi.examples.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_example(midiobserve)
1717
add_example(echo)
1818
add_example(cmidiin)
1919
add_example(cmidiin2)
20+
add_example(lookup)
2021
add_example(midi1_to_midi2)
2122
add_example(midiclock_in)
2223
add_example(midiclock_out)

cmake/libremidi.sources.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ target_sources(libremidi PRIVATE
142142
include/libremidi/input_configuration.hpp
143143
include/libremidi/libremidi.hpp
144144
include/libremidi/message.hpp
145+
include/libremidi/port_comparison.hpp
146+
include/libremidi/port_information.hpp
145147
include/libremidi/output_configuration.hpp
146148
include/libremidi/ump_events.hpp
147149

examples/lookup.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// midiprobe.cpp
2+
//
3+
// Simple program to check MIDI inputs and outputs.
4+
//
5+
// by Gary Scavone, 2003-2012.
6+
7+
#include "utils.hpp"
8+
9+
#include <libremidi/libremidi.hpp>
10+
#include <libremidi/port_comparison.hpp>
11+
12+
#if defined(_WIN32) && __has_include(<winrt/base.h>)
13+
#include <winrt/base.h>
14+
#endif
15+
16+
#include <chrono>
17+
#include <cstdlib>
18+
#include <iostream>
19+
#include <thread>
20+
21+
void lookup_api(libremidi::API api, const libremidi::input_port& searched)
22+
{
23+
std::string_view api_name = libremidi::get_api_display_name(api);
24+
std::cout << "Displaying ports for: " << api_name << std::endl;
25+
26+
// On Windows 10, apparently the MIDI devices aren't exactly available as soon as the app open...
27+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
28+
29+
libremidi::observer midi{
30+
{.track_hardware = true, .track_virtual = true}, libremidi::observer_configuration_for(api)};
31+
32+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
33+
34+
{
35+
// Check inputs.
36+
auto ports = midi.get_input_ports();
37+
auto res = libremidi::find_closest_port(searched, ports);
38+
if (res.found) {
39+
std::cout << "Found: " << *res.port << "\n";
40+
}
41+
}
42+
}
43+
44+
int main()
45+
{
46+
#if defined(_WIN32) && __has_include(<winrt/base.h>)
47+
// Necessary for using WinUWP and WinMIDI, must be done as early as possible in your main()
48+
winrt::init_apartment();
49+
#endif
50+
51+
// This will find the port that is closest to this.
52+
// For instance, given a Launchpad and a Launchpad Mini,
53+
// the launchpad will be returned. Otherwise, the mini will be returned.
54+
libremidi::input_port searched{{.port_name = "launchpad"}};
55+
56+
for (auto& api : libremidi::available_apis())
57+
lookup_api(api, searched);
58+
for (auto& api : libremidi::available_ump_apis())
59+
lookup_api(api, searched);
60+
return 0;
61+
}

examples/midiprobe.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@
1919

2020
void enumerate_api(libremidi::API api)
2121
{
22-
#if defined(_WIN32) && __has_include(<winrt/base.h>)
23-
// Necessary for using WinUWP and WinMIDI, must be done as early as possible in your main()
24-
winrt::init_apartment();
25-
#endif
26-
2722
std::string_view api_name = libremidi::get_api_display_name(api);
2823
std::cout << "Displaying ports for: " << api_name << std::endl;
2924

@@ -58,6 +53,10 @@ void enumerate_api(libremidi::API api)
5853

5954
int main()
6055
{
56+
#if defined(_WIN32) && __has_include(<winrt/base.h>)
57+
// Necessary for using WinUWP and WinMIDI, must be done as early as possible in your main()
58+
winrt::init_apartment();
59+
#endif
6160
for (auto& api : libremidi::available_apis())
6261
enumerate_api(api);
6362
for (auto& api : libremidi::available_ump_apis())

include/libremidi/backends/jack/helpers.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct jack_client
5050
.client = reinterpret_cast<std::uintptr_t>(client),
5151
.port = 0,
5252
.manufacturer = "",
53-
.device_name = "",
53+
.device_name = jack_get_client_name(client),
5454
.port_name = jack_port_name(port),
5555
.display_name = get_port_display_name(port),
5656
}};

include/libremidi/observer_configuration.hpp

Lines changed: 1 addition & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,8 @@
11
#pragma once
2-
#include <libremidi/api.hpp>
3-
#include <libremidi/config.hpp>
4-
#include <libremidi/error.hpp>
5-
#include <libremidi/types.hpp>
6-
7-
#include <array>
8-
#include <compare>
9-
#include <string>
10-
#include <variant>
2+
#include <libremidi/port_information.hpp>
113

124
namespace libremidi
135
{
14-
struct LIBREMIDI_EXPORT port_information
15-
{
16-
// Compat
17-
using port_type = libremidi::transport_type;
18-
19-
/// Which API is this port for. port_information objects are in general
20-
/// not useable for different APIs than the API of the observer that created them.
21-
libremidi::API api{};
22-
23-
/// Handle to the API client object if the API provides one
24-
// ALSA Raw: unused
25-
// ALSA Seq: snd_seq_t*
26-
// CoreMIDI: MidiClientRef
27-
// JACK: jack_client_t*
28-
// PipeWire: unused // FIXME: pw_context? pw_main_loop?
29-
// WebMIDI: unused
30-
// WinMIDI: TODO
31-
// WinMM: unused
32-
// WinUWP: unused
33-
client_handle client = static_cast<client_handle>(-1);
34-
35-
/// Container identifier if the API provides one
36-
// ALSA: device id (std::string), e.g. ID_PATH as returned by udev: "pci-0000:00:14.0-usb-0:12:1.0"
37-
// CoreMIDI: USBLocationID (int32_t)
38-
// WinMIDI: ContainerID GUID (bit_cast to a winapi or winrt::GUID ;
39-
// this is not the string but the binary representation).
40-
container_identifier container = std::monostate{};
41-
42-
/// Device identifier if the API provides one
43-
// ALSA: sysfs path (std::string), e.g. "/sys/devices/pci0000:00/0000:00:02.2/0000:02:00.0/sound/card0/controlC0"
44-
// CoreMIDI: USBVendorProduct (int32_t)
45-
// WinMIDI: EndpointDeviceId (std::string), e.g. "\\?\swd#midisrv#midiu_ksa..."
46-
// WinMM: MIDI{IN,OUT}CAPS mId / pId { uint16_t manufacturer_id, uint16_t product_id; }
47-
device_identifier device = std::monostate{};
48-
49-
/// Handle to the port identifier if the API provides one
50-
// ALSA Raw: bit_cast to struct { uint16_t card, device, sub, padding; }.
51-
// ALSA Seq: bit_cast to struct { uint32_t client, port; }
52-
// CoreMIDI: MidiObjectRef's kMIDIPropertyUniqueID (uint32_t)
53-
// JACK: jack_port_id_t
54-
// PipeWire: port.id
55-
// WebMIDI: index of the MIDI device in the list provided by the browser.
56-
// WinMIDI: uint64_t terminal_block_number; (MidiGroupTerminalBlock::Number(), index is 1-based)
57-
// WinMM: port index between 0 and midi{In,Out}GetNumDevs()
58-
// WinUWP: index of the MIDI device in the list provided by the OS.
59-
port_handle port = static_cast<port_handle>(-1);
60-
61-
/// User-readable information
62-
// ALSA Raw: ID_VENDOR_FROM_DATABASE if provided by udev
63-
// ALSA Seq: ID_VENDOR_FROM_DATABASE if provided by udev
64-
// CoreMIDI: kMIDIPropertyManufacturer
65-
// WinMIDI: MidiEndpointDeviceInformation::GetTransportSuppliedInfo().ManufacturerName
66-
// WinMM: unavailable
67-
std::string manufacturer{};
68-
69-
// ALSA Raw: ID_MODEL_FROM_DATABASE if provided by udev
70-
// ALSA Seq: ID_MODEL_FROM_DATABASE if provided by udev
71-
// WinMIDI: MidiEndpointDeviceInformation::GetTransportSuppliedInfo().Name
72-
std::string product{};
73-
74-
/// "Unique" serial number. Note that this is super unreliable - pretty
75-
/// much no MIDI device manufacturer bothers with unique per-device serial number
76-
/// unlike most USB devices.
77-
// ALSA Raw: ID_USB_SERIAL if provided by udev.
78-
// ALSA Seq: ID_USB_SERIAL if provided by udev.
79-
// WinMIDI: MidiEndpointDeviceInformation::GetTransportSuppliedInfo().SerialNumber
80-
std::string serial{};
81-
82-
// ALSA Raw: Name returned by snd_rawmidi_info_get_name
83-
// ALSA Seq: Name returned by snd_seq_client_info_get_name
84-
// CoreMIDI: kMIDIPropertyModel
85-
// WinMIDI: MidiEndpointDeviceInformation::Name
86-
// WinMM: unavailable
87-
std::string device_name{};
88-
89-
// ALSA Raw: Name returned by snd_rawmidi_info_get_subdevice_name
90-
// ALSA Seq: Name returned by snd_seq_port_info_get_name
91-
// CoreMIDI: kMIDIPropertyName
92-
// WinMIDI: MidiGroupTerminalBlock::Name
93-
// WinMM: szPname
94-
std::string port_name{};
95-
96-
// CoreMIDI: kMIDIPropertyDisplayName
97-
// Otherwise: the closest to a unique name we can get
98-
std::string display_name{};
99-
100-
/// Port type
101-
// CoreMIDI: available
102-
// WinMIDI: available
103-
// WinMM: unavailable
104-
port_type type = port_type::unknown;
105-
106-
// Equality and comparison operators are deleted as there is not one
107-
// single correct way to compare two port_information:
108-
// in some cases it may be useful to only compare the names, while in other cases
109-
// it is necessary to check whether this is the exact same low-level identifier.
110-
// Thus, the end-user must define their own custom equality operators
111-
// if using std:: containers or algorithms
112-
bool operator==(const port_information& other) const noexcept = delete;
113-
std::strong_ordering operator<=>(const port_information& other) const noexcept = delete;
114-
};
115-
116-
struct input_port : port_information
117-
{
118-
bool operator==(const input_port& other) const noexcept = delete;
119-
std::strong_ordering operator<=>(const input_port& other) const noexcept = delete;
120-
};
121-
struct output_port : port_information
122-
{
123-
bool operator==(const output_port& other) const noexcept = delete;
124-
std::strong_ordering operator<=>(const output_port& other) const noexcept = delete;
125-
};
126-
1276
using input_port_callback = std::function<void(const input_port&)>;
1287
using output_port_callback = std::function<void(const output_port&)>;
1298
struct observer_configuration

0 commit comments

Comments
 (0)