upgrade to libremidi 5.1, raylib and raygui latest

This commit is contained in:
Sebastian @ HfG
2025-06-13 19:06:09 +02:00
parent bd642576bb
commit 96bd5f30e6
112 changed files with 6484 additions and 1946 deletions
@@ -0,0 +1,36 @@
#pragma once
#include <libremidi/backends/pipewire/config.hpp>
#include <libremidi/config.hpp>
namespace libremidi::pipewire_ump
{
struct input_configuration
{
std::string client_name = "libremidi client";
pw_main_loop* context{};
pw_filter* filter{};
std::function<void(libremidi::pipewire_callback)> set_process_func{};
std::function<void(int64_t)> clear_process_func{};
};
struct output_configuration
{
std::string client_name = "libremidi client";
pw_main_loop* context{};
pw_filter* filter{};
std::function<void(libremidi::pipewire_callback)> set_process_func{};
std::function<void(int64_t)> clear_process_func{};
int64_t output_buffer_size{65536};
};
struct observer_configuration
{
std::string client_name = "libremidi client";
pw_main_loop* context{};
};
}
@@ -0,0 +1,135 @@
#pragma once
#include <libremidi/backends/pipewire/helpers.hpp>
#include <libremidi/backends/pipewire_ump/config.hpp>
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
namespace libremidi::pipewire_ump
{
class midi_in_pipewire final
: public midi2::in_api
, public pipewire_helpers
, public error_handler
{
public:
struct
: ump_input_configuration
, libremidi::pipewire_ump::input_configuration
{
} configuration;
explicit midi_in_pipewire(
ump_input_configuration&& conf, libremidi::pipewire_ump::input_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
if (auto ret = create_context(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
if (auto ret = create_filter(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
client_open_ = stdx::error{};
}
~midi_in_pipewire() override
{
stop_thread();
do_close_port();
destroy_filter(*this);
destroy_context();
client_open_ = std::errc::not_connected;
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::PIPEWIRE_UMP; }
stdx::error open_port(const input_port& in_port, std::string_view name) override
{
if (auto err = create_local_port(*this, name, SPA_DIRECTION_INPUT, "32 bit raw UMP");
err != stdx::error{})
return err;
if (auto err = link_ports(*this, in_port); err != stdx::error{})
return err;
start_thread();
return stdx::error{};
}
stdx::error open_virtual_port(std::string_view name) override
{
if (auto err = create_local_port(*this, name, SPA_DIRECTION_INPUT, "32 bit raw UMP");
err != stdx::error{})
return err;
start_thread();
return stdx::error{};
}
stdx::error close_port() override
{
stop_thread();
return do_close_port();
}
stdx::error set_port_name(std::string_view port_name) override
{
return rename_port(port_name);
}
timestamp absolute_timestamp() const noexcept override { return system_ns(); }
void process(struct spa_io_position* position)
{
static constexpr timestamp_backend_info timestamp_info{
.has_absolute_timestamps = true,
.absolute_is_monotonic = true,
.has_samples = true,
};
assert(this->filter);
assert(this->filter->port);
const auto b = pw.filter_dequeue_buffer(this->filter->port);
if (!b)
return;
const auto buf = b->buffer;
const auto d = &buf->datas[0];
if (d->data == nullptr)
return;
const auto pod
= (spa_pod*)spa_pod_from_data(d->data, d->maxsize, d->chunk->offset, d->chunk->size);
if (!pod)
return;
if (!spa_pod_is_sequence(pod))
return;
struct spa_pod_control* c{};
SPA_POD_SEQUENCE_FOREACH((struct spa_pod_sequence*)pod, c)
{
if (c->type != SPA_CONTROL_UMP)
continue;
auto data = (uint8_t*)SPA_POD_BODY(&c->value);
auto size = SPA_POD_BODY_SIZE(&c->value);
const auto to_ns = [=, clk = position->clock] {
return 1e9 * ((clk.position + c->offset) / (double)clk.rate.denom);
};
m_processing.on_bytes(
{(uint32_t*)data, (uint32_t*)(data + size)},
m_processing.timestamp<timestamp_info>(to_ns, c->offset));
}
pw.filter_queue_buffer(this->filter->port, b);
}
midi2::input_state_machine m_processing{this->configuration};
};
}
@@ -0,0 +1,185 @@
#pragma once
#include <libremidi/backends/pipewire/helpers.hpp>
#include <libremidi/backends/pipewire_ump/config.hpp>
#include <libremidi/detail/midi_out.hpp>
#include <readerwriterqueue.h>
namespace libremidi::pipewire_ump
{
class midi_out_pipewire
: public midi2::out_api
, public pipewire_helpers
, public error_handler
{
public:
struct
: libremidi::output_configuration
, libremidi::pipewire_ump::output_configuration
{
} configuration;
midi_out_pipewire(
libremidi::output_configuration&& conf,
libremidi::pipewire_ump::output_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
if (auto ret = create_context(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
if (auto ret = create_filter(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
client_open_ = stdx::error{};
}
~midi_out_pipewire() override
{
stop_thread();
do_close_port();
destroy_filter(*this);
destroy_context();
client_open_ = std::errc::not_connected;
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::PIPEWIRE_UMP; }
stdx::error open_port(const output_port& out_port, std::string_view name) override
{
if (auto err = create_local_port(*this, name, SPA_DIRECTION_OUTPUT, "32 bit raw UMP");
err != stdx::error{})
return err;
this->filter->set_port_buffer(configuration.output_buffer_size);
if (auto err = link_ports(*this, out_port); err != stdx::error{})
return err;
start_thread();
return stdx::error{};
}
stdx::error open_virtual_port(std::string_view name) override
{
if (auto err = create_local_port(*this, name, SPA_DIRECTION_OUTPUT, "32 bit raw UMP");
err != stdx::error{})
return err;
this->filter->set_port_buffer(configuration.output_buffer_size);
start_thread();
return stdx::error{};
}
stdx::error close_port() override
{
stop_thread();
return do_close_port();
}
stdx::error set_port_name(std::string_view port_name) override
{
return rename_port(port_name);
}
int process(spa_io_position* pos)
{
m_process_clock.store(pos->clock.nsec, std::memory_order_relaxed);
const auto b = pw.filter_dequeue_buffer(this->filter->port);
if (!b)
return 1;
const auto buf = b->buffer;
const auto d = &buf->datas[0];
if (d->data == nullptr)
return 1;
spa_pod_builder build;
spa_zero(build);
spa_pod_builder_init(&build, d->data, d->maxsize);
spa_pod_frame f;
spa_pod_builder_push_sequence(&build, &f, 0);
// for all events
while (auto m_ptr = m_queue.peek())
{
auto& m = *m_ptr;
spa_pod_builder_control(&build, static_cast<int32_t>(m.timestamp), SPA_CONTROL_UMP);
int res = spa_pod_builder_bytes(&build, m.data, cmidi2_ump_get_message_size_bytes(m.data));
// Try again next buffer
if (res == -ENOSPC)
break;
// Recycle the memory
m_queue.pop();
}
spa_pod_builder_pop(&build, &f);
int n_fill_frames = build.state.offset;
if (n_fill_frames > 0)
{
d->chunk->offset = 0;
d->chunk->stride = 1;
d->chunk->size = n_fill_frames;
b->size = n_fill_frames;
pw.filter_queue_buffer(this->filter->port, b);
return 0;
}
pw.filter_flush(this->filter->filter, true);
return 0;
}
stdx::error send_ump(const uint32_t* message, size_t size) override
{
// FIXME
if (size > 4)
return std::errc::message_size;
libremidi::ump m;
std::copy_n(message, size, m.data);
m.timestamp = 0;
m_queue.enqueue(std::move(m));
return stdx::error{};
}
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
{
// FIXME
if (size > 4)
return std::errc::message_size;
libremidi::ump m;
std::copy_n(message, size, m.data);
m.timestamp = convert_timestamp(ts);
m_queue.enqueue(std::move(m)); // FIXME actually send them scheudled
return stdx::error{};
}
moodycamel::ReaderWriterQueue<libremidi::ump> m_queue;
std::atomic_int64_t m_process_clock = 0;
};
}
@@ -0,0 +1,68 @@
#pragma once
#include <libremidi/backends/pipewire/helpers.hpp>
#include <libremidi/backends/pipewire_ump/config.hpp>
#include <libremidi/detail/observer.hpp>
#include <unordered_set>
namespace libremidi::pipewire_ump
{
class observer_pipewire final
: public observer_api
, private pipewire_helpers
, private error_handler
{
public:
struct
: libremidi::observer_configuration
, libremidi::pipewire_ump::observer_configuration
{
} configuration;
explicit observer_pipewire(
libremidi::observer_configuration&& conf,
libremidi::pipewire_ump::observer_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
create_context(*this);
// FIXME port rename callback
{
this->add_callbacks("UMP", configuration);
this->start_thread();
}
if (configuration.notify_in_constructor)
{
if (configuration.input_added)
for (const auto& p : get_input_ports())
configuration.input_added(p);
if (configuration.output_added)
for (const auto& p : get_output_ports())
configuration.output_added(p);
}
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::PIPEWIRE_UMP; }
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
return get_ports<SPA_DIRECTION_OUTPUT>("UMP", this->configuration, *this->global_context);
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
return get_ports<SPA_DIRECTION_INPUT>("UMP", this->configuration, *this->global_context);
}
~observer_pipewire()
{
stop_thread();
destroy_context();
}
std::unordered_set<std::string> seen_input_ports;
std::unordered_set<std::string> seen_output_ports;
};
}