upgrade to libremidi 5.1, raylib and raygui latest

This commit is contained in:
Sebastian @ HfG
2025-06-13 19:16:35 +02:00
parent 62454960d1
commit 2cc6839009
112 changed files with 6484 additions and 1946 deletions
@@ -0,0 +1,33 @@
#pragma once
#include <libremidi/backends/jack/config.hpp>
namespace libremidi::jack_ump
{
struct input_configuration
{
std::string client_name = "libremidi client";
jack_client_t* context{};
std::function<void(libremidi::jack_callback)> set_process_func{};
std::function<void(int64_t)> clear_process_func{};
};
struct output_configuration
{
std::string client_name = "libremidi client";
jack_client_t* context{};
std::function<void(libremidi::jack_callback)> set_process_func{};
std::function<void(int64_t)> clear_process_func{};
int32_t ringbuffer_size = 16384;
bool direct = false;
};
struct observer_configuration
{
std::string client_name = "libremidi client";
jack_client_t* context{};
};
}
@@ -0,0 +1,119 @@
#pragma once
#include <libremidi/backends/jack/helpers.hpp>
#include <libremidi/backends/jack_ump/config.hpp>
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
namespace libremidi::jack_ump
{
class midi_in_jack final
: public midi2::in_api
, public jack_helpers
, private jack_midi1
, public error_handler
{
public:
using midi_api::client_open_;
struct
: libremidi::ump_input_configuration
, jack_ump::input_configuration
{
} configuration;
explicit midi_in_jack(
libremidi::ump_input_configuration&& conf, jack_ump::input_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
auto status = connect(*this);
if (!this->client)
{
libremidi_handle_error(configuration, "Could not create JACK client");
client_open_ = from_jack_status(status);
return;
}
client_open_ = stdx::error{};
}
~midi_in_jack() override
{
midi_in_jack::close_port();
disconnect(*this);
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_UMP; }
stdx::error open_port(const input_port& port, std::string_view portName) override
{
if (auto err
= create_local_port(*this, portName, port_type, JackPortFlags(JackPortIsInput | 0x20));
err != stdx::error{})
return err;
if (int err = jack_connect(this->client, port.port_name.c_str(), jack_port_name(this->port));
err != 0 && err != EEXIST)
{
libremidi_handle_error(
configuration,
"could not connect to port: " + port.port_name + " -> " + jack_port_name(this->port));
return from_errc(err);
}
return stdx::error{};
}
stdx::error open_virtual_port(std::string_view portName) override
{
return create_local_port(*this, portName, port_type, JackPortFlags(JackPortIsInput | 0x20));
}
stdx::error close_port() override { return do_close_port(); }
stdx::error set_port_name(std::string_view portName) override
{
int ret = jack_port_rename(this->client, this->port, portName.data());
return from_errc(ret);
}
timestamp absolute_timestamp() const noexcept override
{
return 1000 * jack_frames_to_time(client, jack_frame_time(client));
}
int process(jack_nframes_t nframes)
{
static constexpr timestamp_backend_info timestamp_info{
.has_absolute_timestamps = true,
.absolute_is_monotonic = true,
.has_samples = true,
};
void* buff = jack_port_get_buffer(this->port, nframes);
// Timing
jack_nframes_t current_frames{};
jack_time_t current_usecs{}; // roughly CLOCK_MONOTONIC
jack_time_t next_usecs{};
float period_usecs{};
jack_get_cycle_times(
this->client, &current_frames, &current_usecs, &next_usecs, &period_usecs);
// We have midi events in buffer
uint32_t ev_count = jack_midi_get_event_count(buff);
for (uint32_t j = 0; j < ev_count; j++)
{
jack_midi_event_t event{};
jack_midi_event_get(&event, buff, j);
const auto to_ns
= [=, this] { return 1000 * jack_frames_to_time(client, current_frames + event.time); };
m_processing.on_bytes(
{(uint32_t*)event.buffer, (uint32_t*)(event.buffer + event.size)},
m_processing.timestamp<timestamp_info>(to_ns, event.time));
}
return 0;
}
midi2::input_state_machine m_processing{this->configuration};
};
}
@@ -0,0 +1,184 @@
#pragma once
#include <libremidi/backends/jack/helpers.hpp>
#include <libremidi/backends/jack_ump/config.hpp>
#include <libremidi/detail/midi_out.hpp>
namespace libremidi::jack_ump
{
class midi_out_jack
: public midi2::out_api
, public jack_helpers
, private jack_midi1
, public error_handler
{
public:
using midi_api::client_open_;
struct
: libremidi::output_configuration
, jack_ump::output_configuration
{
} configuration;
midi_out_jack(libremidi::output_configuration&& conf, jack_ump::output_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
}
~midi_out_jack() override { }
libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_UMP; }
stdx::error open_port(const output_port& port, std::string_view portName) override
{
if (auto err
= create_local_port(*this, portName, port_type, JackPortFlags(JackPortIsOutput | 0x20));
err != stdx::error{})
return err;
// Connecting to the output
if (int err = jack_connect(this->client, jack_port_name(this->port), port.port_name.c_str());
err != 0 && err != EEXIST)
{
libremidi_handle_error(configuration, "could not connect to port" + port.port_name);
return from_errc(err);
}
return stdx::error{};
}
stdx::error open_virtual_port(std::string_view portName) override
{
return create_local_port(*this, portName, port_type, JackPortFlags(JackPortIsOutput | 0x20));
}
stdx::error close_port() override { return do_close_port(); }
stdx::error set_port_name(std::string_view portName) override
{
int ret = jack_port_rename(this->client, this->port, portName.data());
return from_errc(ret);
}
};
class midi_out_jack_queued final : public midi_out_jack
{
public:
midi_out_jack_queued(
libremidi::output_configuration&& conf, jack_ump::output_configuration&& apiconf)
: midi_out_jack{std::move(conf), std::move(apiconf)}
, m_queue{configuration.ringbuffer_size}
{
auto status = connect(*this);
if (!this->client)
{
libremidi_handle_error(configuration, "Could not create JACK client");
client_open_ = from_jack_status(status);
return;
}
client_open_ = stdx::error{};
}
~midi_out_jack_queued() override
{
midi_out_jack::close_port();
disconnect(*this);
}
stdx::error send_ump(const uint32_t* message, std::size_t size) override
{
return m_queue.write((unsigned char*)message, size * sizeof(uint32_t));
}
int process(jack_nframes_t nframes)
{
void* buff = jack_port_get_buffer(this->port, nframes);
jack_midi_clear_buffer(buff);
this->m_queue.read(buff);
return 0;
}
private:
libremidi::jack_queue m_queue;
};
class midi_out_jack_direct final : public midi_out_jack
{
public:
midi_out_jack_direct(
libremidi::output_configuration&& conf, jack_ump::output_configuration&& apiconf)
: midi_out_jack{std::move(conf), std::move(apiconf)}
{
auto status = connect(*this);
if (!this->client)
{
libremidi_handle_error(configuration, "Could not create JACK client");
client_open_ = from_jack_status(status);
return;
}
buffer_size = jack_get_buffer_size(this->client);
client_open_ = stdx::error{};
}
~midi_out_jack_direct() override
{
midi_out_jack::close_port();
disconnect(*this);
}
int process(jack_nframes_t nframes)
{
void* buff = jack_port_get_buffer(this->port, nframes);
jack_midi_clear_buffer(buff);
return 0;
}
stdx::error send_ump(const uint32_t* message, std::size_t size) override
{
void* buff = jack_port_get_buffer(this->port, buffer_size);
int ret = jack_midi_event_write(buff, 0, (unsigned char*)message, size * sizeof(uint32_t));
return from_errc(ret);
}
int convert_timestamp(int64_t user) const noexcept
{
switch (configuration.timestamps)
{
case timestamp_mode::AudioFrame:
return static_cast<int>(user);
default:
// TODO
return 0;
}
}
stdx::error schedule_ump(int64_t ts, const uint32_t* message, size_t size) override
{
void* buff = jack_port_get_buffer(this->port, buffer_size);
int ret = jack_midi_event_write(
buff, convert_timestamp(ts), (unsigned char*)message, size * sizeof(uint32_t));
return from_errc(ret);
}
int buffer_size{};
};
}
namespace libremidi
{
template <>
inline std::unique_ptr<midi_out_api>
make<jack_ump::midi_out_jack>(output_configuration&& conf, jack_ump::output_configuration&& api)
{
if (api.direct)
return std::make_unique<jack_ump::midi_out_jack_direct>(std::move(conf), std::move(api));
else
return std::make_unique<jack_ump::midi_out_jack_queued>(std::move(conf), std::move(api));
}
}
@@ -0,0 +1,235 @@
#pragma once
#include <libremidi/backends/jack/helpers.hpp>
#include <libremidi/backends/jack_ump/config.hpp>
#include <libremidi/detail/observer.hpp>
#include <unordered_set>
namespace libremidi::jack_ump
{
class observer_jack final
: public observer_api
, private jack_client
, private jack_midi1 // seems like a bug in PipeWire? MIDI 2 flag is set but the MIDI 1 type string is used
, private error_handler
{
public:
struct
: libremidi::observer_configuration
, jack_ump::observer_configuration
{
} configuration;
explicit observer_jack(
libremidi::observer_configuration&& conf, jack_ump::observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
// Initialize JACK client
if (configuration.context)
{
this->client = configuration.context;
set_callbacks();
}
else
{
jack_status_t status{};
this->client
= jack_client_open(configuration.client_name.c_str(), JackNoStartServer, &status);
if (status != jack_status_t{})
libremidi_handle_error(configuration, std::to_string((int)status));
if (this->client != nullptr)
{
set_callbacks();
jack_activate(this->client);
}
}
}
void initial_callback()
{
{
const char** ports = jack_get_ports(client, nullptr, port_type, JackPortIsOutput);
if (ports != nullptr)
{
int i = 0;
while (ports[i] != nullptr)
{
const auto port = jack_port_by_name(client, ports[i]);
const auto flags = jack_port_flags(port);
if (!(flags & 0x20)) // midi 2 check
{
i++;
continue;
}
bool physical = flags & JackPortIsPhysical;
bool ok = configuration.track_any;
if (configuration.track_hardware)
ok |= physical;
if (configuration.track_virtual)
ok |= !physical;
if (ok)
{
seen_input_ports.insert(ports[i]);
if (this->configuration.input_added && configuration.notify_in_constructor)
this->configuration.input_added(to_port_info<true>(client, port));
}
i++;
}
}
jack_free(ports);
}
{
const char** ports = jack_get_ports(client, nullptr, port_type, JackPortIsInput);
if (ports != nullptr)
{
int i = 0;
while (ports[i] != nullptr)
{
const auto port = jack_port_by_name(client, ports[i]);
const auto flags = jack_port_flags(port);
if (!(flags & 0x20)) // midi 2 check
{
i++;
continue;
}
bool physical = flags & JackPortIsPhysical;
bool ok = configuration.track_any;
if (configuration.track_hardware)
ok |= physical;
if (configuration.track_virtual)
ok |= !physical;
if (ok)
{
seen_output_ports.insert(ports[i]);
if (this->configuration.output_added && configuration.notify_in_constructor)
this->configuration.output_added(to_port_info<false>(client, port));
}
i++;
}
}
jack_free(ports);
}
}
void on_port_callback(jack_port_t* port, bool reg)
{
auto flags = jack_port_flags(port);
std::string name = jack_port_name(port);
if (reg)
{
std::string_view type = jack_port_type(port);
if (type != port_type)
return;
if (!(flags & 0x20)) // midi 2 check
return;
bool physical = flags & JackPortIsPhysical;
bool ok = configuration.track_any;
if (configuration.track_hardware)
ok |= physical;
if (configuration.track_virtual)
ok |= !physical;
if (!ok)
return;
// Note: we keep track of the ports as
// when disconnecting, jack_port_type and jack_port_flags aren't correctly
// set anymore.
if (flags & JackPortIsOutput)
{
seen_input_ports.insert(name);
if (this->configuration.input_added)
this->configuration.input_added(to_port_info<true>(client, port));
}
else if (flags & JackPortIsInput)
{
seen_output_ports.insert(name);
if (this->configuration.output_added)
this->configuration.output_added(to_port_info<false>(client, port));
}
}
else
{
if (auto it = seen_input_ports.find(name); it != seen_input_ports.end())
{
if (this->configuration.input_removed)
this->configuration.input_removed(to_port_info<true>(client, port));
seen_input_ports.erase(it);
}
if (auto it = seen_output_ports.find(name); it != seen_output_ports.end())
{
if (this->configuration.output_removed)
this->configuration.output_removed(to_port_info<false>(client, port));
seen_output_ports.erase(it);
}
}
}
void set_callbacks()
{
initial_callback();
if (!configuration.has_callbacks())
return;
jack_set_port_registration_callback(this->client, +[](jack_port_id_t p, int r, void* arg) {
auto& self = *(observer_jack*)arg;
if (auto port = jack_port_by_id(self.client, p))
{
self.on_port_callback(port, r != 0);
}
}, this);
jack_set_port_rename_callback(
this->client,
+[](jack_port_id_t p, const char* /*old_name*/, const char* /*new_name*/, void* arg) {
const auto& self = *static_cast<observer_jack*>(arg);
auto port = jack_port_by_id(self.client, p);
if (!port)
return;
}, this);
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::JACK_UMP; }
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
return get_ports<true>(this->client, nullptr, port_type, JackPortIsOutput, true);
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
return get_ports<false>(this->client, nullptr, port_type, JackPortIsInput, true);
}
~observer_jack()
{
if (client && !configuration.context)
{
// If we own the client, deactivate it
jack_deactivate(this->client);
jack_client_close(this->client);
this->client = nullptr;
}
}
std::unordered_set<std::string> seen_input_ports;
std::unordered_set<std::string> seen_output_ports;
};
}