Files
viseq/include/libremidi/client.hpp
T
2024-10-28 19:24:18 +01:00

205 lines
5.8 KiB
C++

#pragma once
#include <libremidi/configurations.hpp>
#include <libremidi/libremidi.hpp>
#include <libremidi/shared_context.hpp>
#include <map>
namespace libremidi::midi1
{
struct client_configuration
{
libremidi::API api = libremidi::midi1::default_api();
//! A client name, if the backend allows it (e.g. JACK, ALSA SEQ...)
std::string_view client_name = "libremidi client";
//! Set a callback function to be invoked for incoming MIDI messages.
//! Mandatory!
std::function<void(const libremidi::input_port&, message&&)> on_message
= [](const libremidi::input_port& /*port*/, libremidi::message&&) {};
//! Observation callbacks for when ports are added or removed
input_port_callback input_added;
input_port_callback input_removed;
output_port_callback output_added;
output_port_callback output_removed;
//! Error callback function to be invoked when an error has occured.
/*!
The callback function will be called whenever an error has occured. It is
best to set the error callback function before opening a port.
*/
midi_error_callback on_error{};
midi_warning_callback on_warning{};
//! Poll period for observation polling operations, if relevant to the backend
std::chrono::milliseconds poll_period{100};
//! Specify whether certain MIDI message types should be queued or ignored
//! during input.
/*!
By default, MIDI timing and active sensing messages are ignored
during message input because of their relative high data rates.
MIDI sysex messages are ignored by default as well. Variable
values of "true" imply that the respective message type will be
ignored.
*/
uint32_t ignore_sysex : 1 = true;
uint32_t ignore_timing : 1 = true;
uint32_t ignore_sensing : 1 = true;
uint32_t timestamps : 3 = timestamp_mode::Absolute;
//! Observe hardware ports
uint32_t track_hardware : 1 = true;
//! Observe software (virtual) ports if the API provides it
uint32_t track_virtual : 1 = false;
};
class client
{
public:
explicit client(const client_configuration& conf)
: client{conf, create_shared_context(conf.api, conf.client_name)}
{
}
explicit client(const client_configuration& conf, shared_configurations ctx)
: configuration{conf}
, context{ctx}
, m_observer{
observer_configuration{
.on_error = conf.on_error,
.on_warning = conf.on_warning,
.input_added = conf.input_added,
.input_removed = conf.input_removed,
.output_added = conf.output_added,
.output_removed = conf.output_removed,
.track_hardware = conf.track_hardware,
.track_virtual = conf.track_virtual,
.notify_in_constructor = false},
context.observer}
{
if (context.context)
context.context->start_processing();
}
~client()
{
m_inputs.clear();
m_outputs.clear();
if (context.context)
context.context->stop_processing();
}
std::vector<libremidi::input_port> get_input_ports() const noexcept
{
return m_observer.get_input_ports();
}
std::vector<libremidi::output_port> get_output_ports() const noexcept
{
return m_observer.get_output_ports();
}
void add_input(const input_port& port, std::string_view name)
{
if (m_inputs.find(port) != m_inputs.end())
return;
auto res = m_inputs.try_emplace(
port,
input_configuration{
.on_message
= [this,
port](libremidi::message&& m) { configuration.on_message(port, std::move(m)); },
.on_raw_data = {},
.get_timestamp = {},
.on_error = configuration.on_error,
.on_warning = configuration.on_warning,
.ignore_sysex = configuration.ignore_sysex,
.ignore_timing = configuration.ignore_timing,
.ignore_sensing = configuration.ignore_sensing,
.timestamps = configuration.timestamps},
context.in);
res.first->second.open_port(port, name);
}
void add_output(const output_port& port, std::string_view name)
{
if (m_outputs.find(port) != m_outputs.end())
return;
auto res = m_outputs.try_emplace(
port,
output_configuration{
.on_error = configuration.on_error,
.on_warning = configuration.on_warning,
.timestamps = configuration.timestamps},
context.out);
res.first->second.open_port(port, name);
}
void remove_input(const input_port& port) { m_inputs.erase(port); }
void remove_output(const output_port& port) { m_outputs.erase(port); }
stdx::error send_message(const unsigned char* message, size_t size)
{
for (auto& [_, out] : m_outputs)
{
if (auto err = out.send_message(message, size); err != stdx::error{})
return err;
}
return stdx::error{};
}
stdx::error send_ump(const uint32_t* message, size_t size)
{
for (auto& [_, out] : m_outputs)
{
if (auto err = out.send_ump(message, size); err != stdx::error{})
return err;
}
return stdx::error{};
}
stdx::error send_message(const output_port& port, const unsigned char* message, size_t size)
{
if (auto it = m_outputs.find(port); it != m_outputs.end())
return it->second.send_message(message, size);
return stdx::error{};
}
stdx::error send_ump(const output_port& port, const uint32_t* message, size_t size)
{
if (auto it = m_outputs.find(port); it != m_outputs.end())
return it->second.send_ump(message, size);
return stdx::error{};
}
private:
client_configuration configuration;
shared_configurations context;
std::map<input_port, midi_in> m_inputs;
std::map<output_port, midi_out> m_outputs;
observer m_observer;
};
}
#if defined(LIBREMIDI_HEADER_ONLY)
#include "client.cpp"
#endif