Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 37 additions & 30 deletions examples/midiprobe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,45 @@
#include <iostream>
#include <thread>

int main()
void enumerate_api(libremidi::API api)
{
for (auto& api : libremidi::available_apis())
std::string_view api_name = libremidi::get_api_display_name(api);
std::cout << "Displaying ports for: " << api_name << std::endl;

// On Windows 10, apparently the MIDI devices aren't exactly available as soon as the app open...
std::this_thread::sleep_for(std::chrono::milliseconds(100));

libremidi::observer midi{
{.track_hardware = true, .track_virtual = true}, libremidi::observer_configuration_for(api)};

std::this_thread::sleep_for(std::chrono::milliseconds(100));

{
std::string_view api_name = libremidi::get_api_display_name(api);
std::cout << "Displaying ports for: " << api_name << std::endl;

// On Windows 10, apparently the MIDI devices aren't exactly available as soon as the app open...
std::this_thread::sleep_for(std::chrono::milliseconds(100));

libremidi::observer midi{
{.track_hardware = true, .track_virtual = true},
libremidi::observer_configuration_for(api)};
{
// Check inputs.
auto ports = midi.get_input_ports();
std::cout << ports.size() << " MIDI input sources:\n";
int i = 0;
for (auto& port : ports)
std::cout << " - " << i++ << ": " << port << '\n';
}

{
// Check outputs.
auto ports = midi.get_output_ports();
std::cout << ports.size() << " MIDI output sinks:\n";
int i = 0;
for (auto& port : ports)
std::cout << " - " << i++ << ": " << port << '\n';
}

std::cout << "\n";
// Check inputs.
auto ports = midi.get_input_ports();
std::cout << ports.size() << " MIDI input sources:\n";
int i = 0;
for (auto& port : ports)
std::cout << " - " << i++ << ": " << port << '\n';
}

{
// Check outputs.
auto ports = midi.get_output_ports();
std::cout << ports.size() << " MIDI output sinks:\n";
int i = 0;
for (auto& port : ports)
std::cout << " - " << i++ << ": " << port << '\n';
}

std::cout << "\n";
}

int main()
{
for (auto& api : libremidi::available_apis())
enumerate_api(api);
for (auto& api : libremidi::available_ump_apis())
enumerate_api(api);
return 0;
}
6 changes: 3 additions & 3 deletions include/libremidi/backends/alsa_raw/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ struct alsa_raw_port_id
};
inline constexpr port_handle raw_to_port_handle(alsa_raw_port_id id) noexcept
{
return (uint64_t(id.card) << 32) + (uint64_t(id.dev) << 16) + id.port;
return (uint64_t(id.port) << 32) + (uint64_t(id.dev) << 16) + id.card;
}
inline constexpr alsa_raw_port_id raw_from_port_handle(port_handle p) noexcept
{
alsa_raw_port_id ret;
ret.card = (p & 0x00'00'FF'FF'00'00'00'00) >> 32;
ret.port = (p & 0x00'00'FF'FF'00'00'00'00) >> 32;
ret.dev = (p & 0x00'00'00'00'FF'FF'00'00) >> 16;
ret.port = (p & 0x00'00'00'00'00'00'FF'FF);
ret.card = (p & 0x00'00'00'00'00'00'FF'FF);
return ret;
}
static_assert(raw_from_port_handle(raw_to_port_handle({102, 7, 3})).card == 102);
Expand Down
6 changes: 3 additions & 3 deletions include/libremidi/backends/alsa_seq/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ namespace
{
inline constexpr port_handle seq_to_port_handle(uint64_t client, uint64_t port) noexcept
{
return (client << 32) + port;
return (port << 32) + client;
}

inline constexpr std::pair<int, int> seq_from_port_handle(port_handle p) noexcept
{
int client = p >> 32;
int port = p & 0xFFFFFFFF;
int port = p >> 32;
int client = p & 0xFFFFFFFF;
return {client, port};
}

Expand Down
65 changes: 48 additions & 17 deletions include/libremidi/backends/winmidi/observer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,23 @@ class observer_impl final
return libremidi::API::WINDOWS_MIDI_SERVICES;
}

static port_information::port_type code_to_type(std::string_view str) noexcept
{
using enum port_information::port_type;

if (str.starts_with("KS"))
return hardware;
if (str == "BLE")
return port_information::port_type(hardware | bluetooth);
if (str == "VPB" || str == "APP")
return software;
if (str == "LOOP")
return port_information::port_type(software | loopback);
if (str.starts_with("NET"))
return network;
return unknown;
}

template <bool Input>
auto to_port_info(const MidiEndpointDeviceInformation& p, const MidiGroupTerminalBlock& gp)
const noexcept -> std::conditional_t<Input, input_port, output_port>
Expand All @@ -92,11 +109,14 @@ class observer_impl final

return {
{.client = 0,
.container = std::bit_cast<libremidi::uuid>(p.ContainerId()),
.device = to_string(p.EndpointDeviceId()),
.port = gp.Number(),
.manufacturer = to_string(tinfo.ManufacturerName),
.device_name = to_string(p.EndpointDeviceId()),
.device_name = to_string(p.Name()),
.port_name = to_string(gp.Name()),
.display_name = to_string(gp.Name()) + " " + std::to_string(gp.Number())}};
.display_name = to_string(gp.Name()) + " " + std::to_string(gp.Number()),
.type = code_to_type(to_string(p.GetTransportSuppliedInfo().TransportCode))}};
}

std::vector<libremidi::input_port> get_input_ports() const noexcept override
Expand Down Expand Up @@ -143,7 +163,29 @@ class observer_impl final
void on_device_added(
const MidiEndpointDeviceWatcher&, const MidiEndpointDeviceInformationAddedEventArgs& result)
{
const auto& ep = result.AddedDevice();
add_device(result.AddedDevice());
}

void on_device_updated(
const MidiEndpointDeviceWatcher& e,
const MidiEndpointDeviceInformationUpdatedEventArgs& result)
{
// OPTIMIZEME
remove_device(result.EndpointDeviceId());

add_device(
MidiEndpointDeviceInformation::CreateFromEndpointDeviceId(result.EndpointDeviceId()));
}

void on_device_removed(
const MidiEndpointDeviceWatcher&,
const MidiEndpointDeviceInformationRemovedEventArgs& result)
{
remove_device(result.EndpointDeviceId());
}

void add_device(const MidiEndpointDeviceInformation& ep)
{
for (const auto& gp : ep.GetGroupTerminalBlocks())
{
MidiGroupTerminalBlockDirection direction = gp.Direction();
Expand Down Expand Up @@ -195,33 +237,22 @@ class observer_impl final
}
}
}

void on_device_updated(
const MidiEndpointDeviceWatcher&, const MidiEndpointDeviceInformationUpdatedEventArgs&)
{
// FIXME
}

void on_device_removed(
const MidiEndpointDeviceWatcher&,
const MidiEndpointDeviceInformationRemovedEventArgs& result)
void remove_device(hstring eid)
{
std::vector<input_port> to_remove_in;
std::vector<output_port> to_remove_out;

{
std::lock_guard _{m_devices_mtx};
if (auto it = m_known_input_devices.find(result.EndpointDeviceId());
it != m_known_input_devices.end())
if (auto it = m_known_input_devices.find(eid); it != m_known_input_devices.end())
{
for (auto& ip : it->second)
{
to_remove_in.push_back(ip);
}
m_known_input_devices.erase(it);
}
if (auto it = m_known_output_devices.find(result.EndpointDeviceId());
it != m_known_output_devices.end())
if (auto it = m_known_output_devices.find(eid); it != m_known_output_devices.end())
{
for (auto& op : it->second)
{
Expand Down
4 changes: 2 additions & 2 deletions include/libremidi/backends/winmm/helpers.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#pragma once
#include <libremidi/backends/winmm/error_domain.hpp>
#include <libremidi/detail/midi_api.hpp>
#include <algorithm>

#include <string>

namespace libremidi
{
Expand Down Expand Up @@ -70,5 +71,4 @@ inline void MakeUniqueOutPortName(std::string& deviceName, std::size_t portNumbe
deviceName += " ";
deviceName += std::to_string(x);
}

}
3 changes: 0 additions & 3 deletions include/libremidi/backends/winmm/observer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
#include <libremidi/backends/winmm/helpers.hpp>
#include <libremidi/detail/observer.hpp>

#include <condition_variable>
#include <mutex>
#include <ranges>
#include <thread>

namespace libremidi
Expand Down
60 changes: 55 additions & 5 deletions include/libremidi/observer_configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <libremidi/config.hpp>
#include <libremidi/error.hpp>

#include <array>
#include <compare>
#include <string>

Expand All @@ -10,8 +11,33 @@ namespace libremidi
using client_handle = std::uint64_t;
using port_handle = std::uint64_t;

struct uuid
{
std::array<uint8_t, 16> bytes;

bool operator==(const uuid& other) const noexcept = default;
std::strong_ordering operator<=>(const uuid& other) const noexcept = default;
};
using container_handle = std::variant<std::monostate, uuid, std::uint64_t>;
using device_handle = std::variant<std::monostate, std::string, std::uint64_t>;

struct LIBREMIDI_EXPORT port_information
{
enum port_type : uint8_t
{
unknown = 0,

software = (1 << 1),
loopback = (1 << 2),

hardware = (1 << 3),
usb = (1 << 4),
bluetooth = (1 << 5),
pci = (1 << 6),

network = (1 << 7),
};

/// Handle to the API client object if the API provides one
// ALSA Raw: unused
// ALSA Seq: snd_seq_t*
Expand All @@ -23,25 +49,49 @@ struct LIBREMIDI_EXPORT port_information
// WinUWP: unused
client_handle client = static_cast<client_handle>(-1);

/// Container identifier if the API provides one
// WinMIDI: ContainerID GUID (bit_cast to a winapi or winrt::GUID ;
// this is not the string but the binary representation).
// CoreMidi: LocationID (int32_t)
container_handle container = std::monostate{};

/// Device identifier if the API provides one
// WinMM: { uint16_t manufacturer_id, uint16_t product_id; }
// WinMIDI: EndpointDeviceId (std::string), e.g. "\\?\swd#midisrv#midiu_ksa..."
device_handle device = std::monostate{};

/// Handle to the port identifier if the API provides one
// ALSA Raw: { uint16_t card, device, sub, padding; }
// ALSA Seq: { uint32_t client, port; }
// ALSA Raw: bit_cast to struct { uint16_t card, device, sub, padding; }.
// ALSA Seq: bit_cast to struct { uint32_t client, port; }
// CoreMIDI: MidiObjectRef's kMIDIPropertyUniqueID (uint32_t)
// WebMIDI: unused
// JACK: jack_port_id_t
// PipeWire: port.id
// WinMIDI: { uint64_t terminal_block_number; } (MidiGroupTerminalBlock::Number())
// WinMM: unset, identified by port_name
// WinMIDI: uint64_t terminal_block_number; (MidiGroupTerminalBlock::Number(), index is 1-based)
// WinMM: port index between 0 and midi{In,Out}GetNumDevs()
// WinUWP: unused
port_handle port = static_cast<port_handle>(-1);

/// User-readable information
// WinMIDI: MidiEndpointDeviceInformation::GetTransportSuppliedInfo().ManufacturerName
// WinMM: unavailable
std::string manufacturer{};

// WinMIDI: EndpointDeviceId
// WinMIDI: MidiEndpointDeviceInformation::Name
// WinMM: unavailable
std::string device_name{};

// WinMIDI: MidiGroupTerminalBlock::Name
// WinMM: szPname
std::string port_name{};

std::string display_name{};

/// Port type
// WinMM: unavailable
// WinMIDI: available
port_type type = port_type::unknown;

bool operator==(const port_information& other) const noexcept = default;
std::strong_ordering operator<=>(const port_information& other) const noexcept = default;
};
Expand Down