libremidi update to v5.4.3 and added virtual midi out as default
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include <libremidi/config.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
@@ -9,12 +10,12 @@
|
||||
|
||||
#if __has_include(<poll.h>)
|
||||
#include <poll.h>
|
||||
namespace libremidi
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
using poll_descriptors = pollfd;
|
||||
}
|
||||
#else
|
||||
namespace libremidi
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
struct poll_descriptors
|
||||
{
|
||||
@@ -25,7 +26,7 @@ struct poll_descriptors
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace libremidi
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,10 +2,17 @@
|
||||
#include <libremidi/backends/linux/alsa.hpp>
|
||||
#include <libremidi/config.hpp>
|
||||
#include <libremidi/detail/observer.hpp>
|
||||
#include <libremidi/observer_configuration.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
// Credits: greatly inspired from
|
||||
@@ -13,9 +20,7 @@
|
||||
// https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c
|
||||
// Thanks Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||
|
||||
namespace libremidi
|
||||
{
|
||||
namespace
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
struct alsa_raw_port_id
|
||||
{
|
||||
@@ -27,25 +32,25 @@ 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);
|
||||
static_assert(raw_from_port_handle(raw_to_port_handle({12, 7, 3})).dev == 7);
|
||||
static_assert(raw_from_port_handle(raw_to_port_handle({12, 7, 3})).port == 3);
|
||||
}
|
||||
|
||||
namespace alsa_raw
|
||||
{
|
||||
struct alsa_raw_port_info
|
||||
{
|
||||
// hw:1,2,0
|
||||
std::string device;
|
||||
std::string card_name;
|
||||
std::string device_name;
|
||||
@@ -91,7 +96,6 @@ struct enumerator
|
||||
: handler{self}
|
||||
, configuration{self.configuration}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// 1: is an input / output
|
||||
@@ -114,8 +118,9 @@ struct enumerator
|
||||
else if (status < 0 && status != -ENXIO)
|
||||
{
|
||||
handler.libremidi_handle_error(
|
||||
configuration, "Cannot get rawmidi information: " + device_identifier(card, device, sub)
|
||||
+ " : " + snd.strerror(status));
|
||||
configuration,
|
||||
"Cannot get rawmidi information: " + device_identifier(card, device, sub) + " : "
|
||||
+ snd.strerror(status));
|
||||
return status;
|
||||
}
|
||||
else
|
||||
@@ -201,7 +206,8 @@ inline snd_ctl_wrapper::snd_ctl_wrapper(enumerator& self, const char* name)
|
||||
if (status < 0)
|
||||
{
|
||||
self.handler.libremidi_handle_error(
|
||||
self.configuration, "cannot open control for card"s + name + " : " + snd.strerror(status));
|
||||
self.configuration,
|
||||
"cannot open control for card"s + name + " : " + snd.strerror(status));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace libremidi::alsa_raw
|
||||
NAMESPACE_LIBREMIDI::alsa_raw
|
||||
{
|
||||
class midi_in_impl
|
||||
: public midi1::in_api
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
midi_in_alsa_raw_threaded(input_configuration&& conf, alsa_raw_input_configuration&& apiconf)
|
||||
: midi_in_impl{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
if (this->termination_event < 0)
|
||||
if (this->m_termination_event < 0)
|
||||
{
|
||||
libremidi_handle_error(this->configuration, "error creating eventfd.");
|
||||
return;
|
||||
@@ -220,7 +220,7 @@ public:
|
||||
private:
|
||||
void run_thread(auto parse_func)
|
||||
{
|
||||
fds_.push_back(this->termination_event);
|
||||
fds_.push_back(this->m_termination_event);
|
||||
const auto period
|
||||
= std::chrono::duration_cast<std::chrono::milliseconds>(this->configuration.poll_period)
|
||||
.count();
|
||||
@@ -228,12 +228,12 @@ private:
|
||||
for (;;)
|
||||
{
|
||||
// Poll
|
||||
ssize_t err = poll(fds_.data(), fds_.size(), period);
|
||||
ssize_t err = poll(fds_.data(), fds_.size(), static_cast<int32_t>(period));
|
||||
if (err == -EAGAIN)
|
||||
continue;
|
||||
else if (err < 0)
|
||||
return;
|
||||
else if (termination_event.ready(fds_.back()))
|
||||
else if (m_termination_event.ready(fds_.back()))
|
||||
break;
|
||||
|
||||
err = do_read_events(parse_func, {fds_.data(), fds_.size() - 1});
|
||||
@@ -250,11 +250,11 @@ private:
|
||||
{
|
||||
if (configuration.timestamps == timestamp_mode::NoTimestamp)
|
||||
{
|
||||
this->thread_ = std::thread{[this] { run_thread(&midi_in_impl::read_input_buffer); }};
|
||||
this->m_thread = std::thread{[this] { run_thread(&midi_in_impl::read_input_buffer); }};
|
||||
}
|
||||
else
|
||||
{
|
||||
this->thread_ = std::thread{
|
||||
this->m_thread = std::thread{
|
||||
[this] { run_thread(&midi_in_impl::read_input_buffer_with_timestamps); }};
|
||||
}
|
||||
return stdx::error{};
|
||||
@@ -264,8 +264,7 @@ private:
|
||||
using namespace std::literals;
|
||||
|
||||
libremidi_handle_error(
|
||||
this->configuration,
|
||||
"error starting MIDI input thread: "s + e.what());
|
||||
this->configuration, "error starting MIDI input thread: "s + e.what());
|
||||
return e.code();
|
||||
}
|
||||
return stdx::error{};
|
||||
@@ -282,16 +281,16 @@ private:
|
||||
|
||||
stdx::error close_port() override
|
||||
{
|
||||
termination_event.notify();
|
||||
if (thread_.joinable())
|
||||
thread_.join();
|
||||
termination_event.consume(); // Reset to zero
|
||||
m_termination_event.notify();
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
m_termination_event.consume(); // Reset to zero
|
||||
|
||||
return midi_in_impl::close_port();
|
||||
}
|
||||
|
||||
std::thread thread_;
|
||||
eventfd_notifier termination_event{};
|
||||
std::thread m_thread;
|
||||
eventfd_notifier m_termination_event{};
|
||||
};
|
||||
|
||||
class midi_in_alsa_raw_manual : public midi_in_impl
|
||||
@@ -316,19 +315,21 @@ private:
|
||||
{
|
||||
if (configuration.timestamps == timestamp_mode::NoTimestamp)
|
||||
{
|
||||
configuration.manual_poll(manual_poll_parameters{
|
||||
.fds = {this->fds_.data(), this->fds_.size()},
|
||||
.callback = [this](std::span<pollfd> fds) {
|
||||
return do_read_events(&midi_in_impl::read_input_buffer, fds);
|
||||
}});
|
||||
configuration.manual_poll(
|
||||
manual_poll_parameters{
|
||||
.fds = {this->fds_.data(), this->fds_.size()},
|
||||
.callback = [this](std::span<pollfd> fds) {
|
||||
return do_read_events(&midi_in_impl::read_input_buffer, fds);
|
||||
}});
|
||||
}
|
||||
else
|
||||
{
|
||||
configuration.manual_poll(manual_poll_parameters{
|
||||
.fds = {this->fds_.data(), this->fds_.size()},
|
||||
.callback = [this](std::span<pollfd> fds) {
|
||||
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
|
||||
}});
|
||||
configuration.manual_poll(
|
||||
manual_poll_parameters{
|
||||
.fds = {this->fds_.data(), this->fds_.size()},
|
||||
.callback = [this](std::span<pollfd> fds) {
|
||||
return do_read_events(&midi_in_impl::read_input_buffer_with_timestamps, fds);
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +343,7 @@ private:
|
||||
};
|
||||
}
|
||||
|
||||
namespace libremidi
|
||||
NAMESPACE_LIBREMIDI
|
||||
{
|
||||
template <>
|
||||
inline std::unique_ptr<midi_in_api> make<alsa_raw::midi_in_impl>(
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
namespace libremidi::alsa_raw
|
||||
NAMESPACE_LIBREMIDI::alsa_raw
|
||||
{
|
||||
class midi_out_impl final
|
||||
: public midi1::out_api
|
||||
@@ -44,8 +44,7 @@ public:
|
||||
int status = snd.rawmidi.open(NULL, &midiport_, portname, mode);
|
||||
if (status < 0)
|
||||
{
|
||||
libremidi_handle_error(
|
||||
this->configuration, "cannot open device.");
|
||||
libremidi_handle_error(this->configuration, "cannot open device.");
|
||||
return from_errc(status);
|
||||
}
|
||||
return stdx::error{};
|
||||
@@ -87,8 +86,7 @@ public:
|
||||
{
|
||||
if (auto err = snd.rawmidi.write(midiport_, message, size); err < 0)
|
||||
{
|
||||
libremidi_handle_error(
|
||||
this->configuration, "cannot write message.");
|
||||
libremidi_handle_error(this->configuration, "cannot write message.");
|
||||
return from_errc(err);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#if LIBREMIDI_HAS_UDEV
|
||||
#include <libremidi/backends/linux/helpers.hpp>
|
||||
#include <libremidi/backends/linux/udev.hpp>
|
||||
#include <libremidi/detail/observer.hpp>
|
||||
#endif
|
||||
#include <libremidi/detail/observer.hpp>
|
||||
|
||||
namespace libremidi::alsa_raw
|
||||
NAMESPACE_LIBREMIDI::alsa_raw
|
||||
{
|
||||
template <typename Enumerator>
|
||||
class observer_impl_base
|
||||
@@ -27,28 +28,39 @@ public:
|
||||
explicit observer_impl_base(
|
||||
observer_configuration&& conf, alsa_raw_observer_configuration&& apiconf)
|
||||
: configuration{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
}
|
||||
|
||||
~observer_impl_base()
|
||||
{
|
||||
#if LIBREMIDI_HAS_UDEV
|
||||
m_termination_event.notify();
|
||||
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Must be called for child classes due to virtual function calls
|
||||
void finish_init()
|
||||
{
|
||||
if (!configuration.has_callbacks())
|
||||
return;
|
||||
|
||||
fds[0] = this->udev;
|
||||
fds[1] = termination_event;
|
||||
fds[2] = timer_fd;
|
||||
#if LIBREMIDI_HAS_UDEV
|
||||
m_fds[0] = this->m_udev;
|
||||
m_fds[1] = m_termination_event;
|
||||
m_fds[2] = m_timer_fd;
|
||||
#endif
|
||||
|
||||
// Set-up initial state
|
||||
if (configuration.notify_in_constructor)
|
||||
this->check_devices();
|
||||
|
||||
#if LIBREMIDI_HAS_UDEV
|
||||
// Start thread
|
||||
thread = std::thread{[this] { this->run(); }};
|
||||
}
|
||||
|
||||
~observer_impl_base()
|
||||
{
|
||||
termination_event.notify();
|
||||
|
||||
if (thread.joinable())
|
||||
thread.join();
|
||||
m_thread = std::thread{[this] { this->run(); }};
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<libremidi::input_port> get_input_ports() const noexcept override
|
||||
@@ -77,12 +89,12 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
#if LIBREMIDI_HAS_UDEV
|
||||
void run()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (int err = poll(fds, 3, -1); err < 0)
|
||||
if (int err = poll(m_fds, 3, -1); err < 0)
|
||||
{
|
||||
if (err == -EAGAIN)
|
||||
continue;
|
||||
@@ -90,59 +102,87 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
// Check udev
|
||||
if (fds[0].revents & POLLIN)
|
||||
// Check eventfd to see if we need to exit
|
||||
if (m_fds[1].revents & POLLIN)
|
||||
{
|
||||
udev_device* dev = udev.udev.monitor_receive_device(udev.monitor);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check udev
|
||||
if (m_fds[0].revents & POLLIN)
|
||||
{
|
||||
udev_device* dev = m_udev.udev.monitor_receive_device(m_udev.monitor);
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
std::string_view act = udev.udev.device_get_action(dev);
|
||||
std::string_view ss = udev.udev.device_get_subsystem(dev);
|
||||
std::string_view act = m_udev.udev.device_get_action(dev);
|
||||
std::string_view ss = m_udev.udev.device_get_subsystem(dev);
|
||||
if (!act.empty() && ss == "snd_seq")
|
||||
{
|
||||
if (act == "add" || act == "remove")
|
||||
{
|
||||
// Check every 100 milliseconds for ten seconds
|
||||
this->timer_fd.restart(configuration.poll_period.count());
|
||||
timer_check_counts = 100;
|
||||
this->m_timer_fd.restart(configuration.poll_period.count());
|
||||
m_timer_check_counts = 100;
|
||||
}
|
||||
}
|
||||
|
||||
udev.udev.device_unref(dev);
|
||||
m_udev.udev.device_unref(dev);
|
||||
|
||||
fds[0].revents = 0;
|
||||
}
|
||||
|
||||
// Check eventfd
|
||||
if (fds[1].revents & POLLIN)
|
||||
{
|
||||
break;
|
||||
m_fds[0].revents = 0;
|
||||
}
|
||||
|
||||
// Check timer
|
||||
if (fds[2].revents & POLLIN)
|
||||
if (m_fds[2].revents & POLLIN)
|
||||
{
|
||||
if (this->timer_check_counts-- <= 0)
|
||||
this->timer_fd.cancel();
|
||||
fds[2].revents = 0;
|
||||
if (this->m_timer_check_counts-- <= 0)
|
||||
this->m_timer_fd.cancel();
|
||||
m_fds[2].revents = 0;
|
||||
|
||||
check_devices();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <bool Input>
|
||||
auto to_port_info(alsa_raw::alsa_raw_port_info p) const noexcept
|
||||
auto to_port_info(const alsa_raw::alsa_raw_port_info& p) const noexcept
|
||||
-> std::conditional_t<Input, input_port, output_port>
|
||||
{
|
||||
#if LIBREMIDI_HAS_UDEV
|
||||
auto [container, device, type, manufacturer, product, serial]
|
||||
= get_udev_soundcard_info(m_udev, p.card);
|
||||
#else
|
||||
container_identifier container{};
|
||||
device_identifier device{};
|
||||
libremidi::transport_type type{};
|
||||
std::string manufacturer;
|
||||
std::string product;
|
||||
std::string serial;
|
||||
#endif
|
||||
std::string display_name = p.subdevice_name;
|
||||
if (display_name.empty())
|
||||
display_name = p.device_name;
|
||||
if (display_name.empty())
|
||||
display_name = p.card_name;
|
||||
if (display_name.empty())
|
||||
display_name = product;
|
||||
if (display_name.empty())
|
||||
display_name = "unknown";
|
||||
|
||||
return {
|
||||
{.client = 0,
|
||||
{.api = get_current_api(),
|
||||
.client = 0,
|
||||
.container = container,
|
||||
.device = device,
|
||||
.port = raw_to_port_handle({p.card, p.dev, p.sub}),
|
||||
.manufacturer = p.card_name,
|
||||
.manufacturer = manufacturer,
|
||||
.product = product,
|
||||
.serial = serial,
|
||||
.device_name = p.device_name,
|
||||
.port_name = p.subdevice_name,
|
||||
.display_name = p.subdevice_name}};
|
||||
.display_name = std::move(display_name),
|
||||
.type = type}};
|
||||
}
|
||||
|
||||
void check_devices()
|
||||
@@ -151,7 +191,7 @@ private:
|
||||
|
||||
new_devs.enumerate_cards();
|
||||
|
||||
for (auto& in_prev : current_inputs)
|
||||
for (auto& in_prev : m_current_inputs)
|
||||
{
|
||||
if (auto it = std::find(new_devs.inputs.begin(), new_devs.inputs.end(), in_prev);
|
||||
it == new_devs.inputs.end())
|
||||
@@ -165,8 +205,8 @@ private:
|
||||
|
||||
for (auto& in_next : new_devs.inputs)
|
||||
{
|
||||
if (auto it = std::find(current_inputs.begin(), current_inputs.end(), in_next);
|
||||
it == current_inputs.end())
|
||||
if (auto it = std::find(m_current_inputs.begin(), m_current_inputs.end(), in_next);
|
||||
it == m_current_inputs.end())
|
||||
{
|
||||
if (auto& cb = this->configuration.input_added)
|
||||
{
|
||||
@@ -175,7 +215,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& out_prev : current_outputs)
|
||||
for (auto& out_prev : m_current_outputs)
|
||||
{
|
||||
if (auto it = std::find(new_devs.outputs.begin(), new_devs.outputs.end(), out_prev);
|
||||
it == new_devs.outputs.end())
|
||||
@@ -189,8 +229,8 @@ private:
|
||||
|
||||
for (auto& out_next : new_devs.outputs)
|
||||
{
|
||||
if (auto it = std::find(current_outputs.begin(), current_outputs.end(), out_next);
|
||||
it == current_outputs.end())
|
||||
if (auto it = std::find(m_current_outputs.begin(), m_current_outputs.end(), out_next);
|
||||
it == m_current_outputs.end())
|
||||
{
|
||||
if (auto& cb = this->configuration.output_added)
|
||||
{
|
||||
@@ -198,43 +238,34 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
current_inputs = std::move(new_devs.inputs);
|
||||
current_outputs = std::move(new_devs.outputs);
|
||||
m_current_inputs = std::move(new_devs.inputs);
|
||||
m_current_outputs = std::move(new_devs.outputs);
|
||||
}
|
||||
|
||||
udev_helper udev{};
|
||||
eventfd_notifier termination_event{};
|
||||
timerfd_timer timer_fd{};
|
||||
int timer_check_counts = 0;
|
||||
std::thread thread;
|
||||
std::vector<alsa_raw_port_info> current_inputs;
|
||||
std::vector<alsa_raw_port_info> current_outputs;
|
||||
|
||||
pollfd fds[3]{};
|
||||
};
|
||||
}
|
||||
#else
|
||||
#include <libremidi/backends/dummy.hpp>
|
||||
namespace libremidi::alsa_raw
|
||||
{
|
||||
template <typename Enumerator>
|
||||
struct observer_impl_base : observer_dummy
|
||||
{
|
||||
explicit observer_impl_base(
|
||||
[[maybe_unused]] observer_configuration&& conf,
|
||||
[[maybe_unused]] alsa_raw_observer_configuration&& apiconf)
|
||||
: observer_dummy{dummy_configuration{}, dummy_configuration{}}
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
#if LIBREMIDI_HAS_UDEV
|
||||
udev_helper m_udev{};
|
||||
eventfd_notifier m_termination_event{};
|
||||
timerfd_timer m_timer_fd{};
|
||||
int m_timer_check_counts = 0;
|
||||
std::thread m_thread;
|
||||
pollfd m_fds[3]{};
|
||||
#endif
|
||||
|
||||
namespace libremidi::alsa_raw
|
||||
std::vector<alsa_raw_port_info> m_current_inputs;
|
||||
std::vector<alsa_raw_port_info> m_current_outputs;
|
||||
};
|
||||
}
|
||||
|
||||
NAMESPACE_LIBREMIDI::alsa_raw
|
||||
{
|
||||
struct observer_impl : observer_impl_base<alsa_raw::midi1_enumerator>
|
||||
{
|
||||
using alsa_raw::observer_impl_base<midi1_enumerator>::observer_impl_base;
|
||||
observer_impl(observer_configuration&& conf, alsa_raw_observer_configuration&& apiconf)
|
||||
: observer_impl_base<alsa_raw::midi1_enumerator>{std::move(conf), std::move(apiconf)}
|
||||
{
|
||||
finish_init();
|
||||
}
|
||||
|
||||
libremidi::API get_current_api() const noexcept override { return libremidi::API::ALSA_RAW; }
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user