Files
viseq/include/libremidi/libremidi-c.cpp
T

508 lines
13 KiB
C++
Raw Normal View History

2024-10-28 19:24:18 +01:00
// clang-format off
#include <libremidi/libremidi-c.h>
#include <libremidi/libremidi.hpp>
#include <libremidi/backends.hpp>
// clang-format on
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <limits>
struct libremidi_midi_observer_handle
{
libremidi::observer self;
};
struct libremidi_midi_in_handle
{
libremidi::midi_in self;
};
struct libremidi_midi_out_handle
{
libremidi::midi_out self;
};
namespace libremidi
{
static void assign_error_callback(const auto& src, auto& dst)
{
if (src.callback)
{
dst = [src](std::string_view errorText, const source_location& loc) {
src.callback(src.context, errorText.data(), errorText.size(), &loc);
};
}
}
}
extern "C" {
const char* libremidi_get_version(void)
{
return LIBREMIDI_VERSION;
}
void libremidi_available_midi1_apis(void* ctx, void (*cb)(void* ctx, libremidi_api))
{
if (!cb)
return;
libremidi::midi1::for_all_backends([=](auto b) { cb(ctx, b.API); });
}
void libremidi_available_midi2_apis(void* ctx, void (*cb)(void* ctx, libremidi_api))
{
if (!cb)
return;
libremidi::midi2::for_all_backends([=](auto b) { cb(ctx, b.API); });
}
const char* libremidi_api_identifier(libremidi_api api)
{
return libremidi::get_api_name(api).data();
}
const char* libremidi_api_display_name(libremidi_api api)
{
return libremidi::get_api_display_name(api).data();
}
libremidi_api libremidi_get_compiled_api_by_identifier(const char* name)
{
libremidi_api ret = libremidi_api::UNSPECIFIED;
libremidi::midi_any::for_all_backends([&](auto& b) {
if (name == b.name)
ret = b.API;
});
return ret;
}
int libremidi_midi_api_configuration_init(libremidi_api_configuration* conf)
{
memset(conf, 0, sizeof(*conf));
return 0;
}
int libremidi_midi_observer_configuration_init(libremidi_observer_configuration* conf)
{
memset(conf, 0, sizeof(*conf));
return 0;
}
int libremidi_midi_configuration_init(libremidi_midi_configuration* conf)
{
memset(conf, 0, sizeof(*conf));
return 0;
}
int libremidi_midi_in_port_clone(const libremidi_midi_in_port* port, libremidi_midi_in_port** dst)
{
if (!port || !dst)
return -EINVAL;
auto copied = new libremidi::input_port{*reinterpret_cast<const libremidi::input_port*>(port)};
*dst = reinterpret_cast<libremidi_midi_in_port*>(copied);
return 0;
}
int libremidi_midi_in_port_free(libremidi_midi_in_port* port)
{
delete reinterpret_cast<libremidi::input_port*>(port);
return 0;
}
int libremidi_midi_in_port_name(const libremidi_midi_in_port* port, const char** name, size_t* len)
{
if (!port || !name || !len)
return -EINVAL;
auto& p = *reinterpret_cast<const libremidi::input_port*>(port);
*name = p.port_name.data();
*len = p.port_name.size();
return 0;
}
int libremidi_midi_out_port_clone(
const libremidi_midi_out_port* port, libremidi_midi_out_port** dst)
{
if (!port || !dst)
return -EINVAL;
auto copied = new libremidi::output_port{*reinterpret_cast<const libremidi::output_port*>(port)};
*dst = reinterpret_cast<libremidi_midi_out_port*>(copied);
return 0;
}
int libremidi_midi_out_port_free(libremidi_midi_out_port* port)
{
delete reinterpret_cast<libremidi::output_port*>(port);
return 0;
}
int libremidi_midi_out_port_name(
const libremidi_midi_out_port* port, const char** name, size_t* len)
{
if (!port || !name || !len)
return -EINVAL;
auto& p = *reinterpret_cast<const libremidi::output_port*>(port);
*name = p.port_name.data();
*len = p.port_name.size();
return 0;
}
int libremidi_midi_observer_new(
const libremidi_observer_configuration* c, libremidi_api_configuration* api,
libremidi_midi_observer_handle** out)
{
if (!out || !c)
return -EINVAL;
libremidi::observer_configuration conf;
libremidi::assign_error_callback(c->on_error, conf.on_error);
libremidi::assign_error_callback(c->on_warning, conf.on_warning);
conf.track_hardware = c->track_hardware;
conf.track_virtual = c->track_virtual;
conf.track_any = c->track_any;
conf.notify_in_constructor = c->notify_in_constructor;
auto api_conf = libremidi::observer_configuration_for(static_cast<libremidi::API>(api->api));
if (c->input_added.callback)
{
conf.input_added = [cb = c->input_added](const auto& port) {
cb.callback(cb.context, reinterpret_cast<const libremidi_midi_in_port*>(&port));
};
}
if (c->input_removed.callback)
{
conf.input_removed = [cb = c->input_removed](const auto& port) {
cb.callback(cb.context, reinterpret_cast<const libremidi_midi_in_port*>(&port));
};
}
if (c->output_added.callback)
{
conf.output_added = [cb = c->output_added](const auto& port) {
cb.callback(cb.context, reinterpret_cast<const libremidi_midi_out_port*>(&port));
};
}
if (c->output_removed.callback)
{
conf.output_removed = [cb = c->output_removed](const auto& port) {
cb.callback(cb.context, reinterpret_cast<const libremidi_midi_out_port*>(&port));
};
}
try
{
auto ptr = new libremidi_midi_observer_handle{
libremidi::observer{std::move(conf), std::move(api_conf)}};
*out = ptr;
return 0;
}
catch (...)
{
return -EINVAL;
}
}
int libremidi_midi_observer_enumerate_input_ports(
libremidi_midi_observer_handle* ptr, void* context,
void (*cb)(void* ctx, const libremidi_midi_in_port*))
{
if (!ptr || !cb)
return -EINVAL;
for (const auto& port : ptr->self.get_input_ports())
{
cb(context, reinterpret_cast<const libremidi_midi_in_port*>(&port));
}
return 0;
}
int libremidi_midi_observer_enumerate_output_ports(
libremidi_midi_observer_handle* ptr, void* context,
void (*cb)(void* ctx, const libremidi_midi_out_port*))
{
if (!ptr || !cb)
return -EINVAL;
for (const auto& port : ptr->self.get_output_ports())
{
cb(context, reinterpret_cast<const libremidi_midi_out_port*>(&port));
}
return 0;
}
int libremidi_midi_observer_free(libremidi_midi_observer_handle* ptr)
{
delete ptr;
return 0;
}
int libremidi_midi_in_new(
const libremidi_midi_configuration* c, const libremidi_api_configuration* api,
libremidi_midi_in_handle** out)
{
if (!out || !c)
return -EINVAL;
if (!c->virtual_port && !c->in_port)
return -EINVAL;
*out = nullptr;
auto api_conf = libremidi::midi_in_configuration_for(static_cast<libremidi::API>(api->api));
// Create the MIDI object
switch (c->version)
{
case libremidi_midi_configuration::MIDI1:
case libremidi_midi_configuration::MIDI1_RAW: {
libremidi::input_configuration conf;
libremidi::assign_error_callback(c->on_error, conf.on_error);
libremidi::assign_error_callback(c->on_warning, conf.on_warning);
conf.ignore_sensing = c->ignore_sensing;
conf.ignore_sysex = c->ignore_sysex;
conf.ignore_timing = c->ignore_timing;
conf.timestamps = c->timestamps;
if (c->get_timestamp.callback)
{
conf.get_timestamp
= [cb = c->get_timestamp](int64_t msg) { return cb.callback(cb.context, msg); };
}
if (c->on_midi1_message.callback)
conf.on_message = [cb = c->on_midi1_message](const libremidi::message& msg) {
cb.callback(cb.context, msg.timestamp, msg.bytes.data(), msg.size());
};
else if (c->on_midi1_raw_data.callback)
{
conf.on_raw_data = [cb = c->on_midi1_raw_data](std::span<const uint8_t> msg, int64_t ts) {
cb.callback(cb.context, ts, msg.data(), msg.size());
};
}
else
{
return -EINVAL;
}
try
{
auto ptr = new libremidi_midi_in_handle{
libremidi::midi_in{std::move(conf), std::move(api_conf)}};
*out = ptr;
}
catch (...)
{
return -EINVAL;
}
break;
}
case libremidi_midi_configuration::MIDI2:
case libremidi_midi_configuration::MIDI2_RAW: {
libremidi::ump_input_configuration conf;
libremidi::assign_error_callback(c->on_error, conf.on_error);
libremidi::assign_error_callback(c->on_warning, conf.on_warning);
conf.ignore_sensing = c->ignore_sensing;
conf.ignore_sysex = c->ignore_sysex;
conf.ignore_timing = c->ignore_timing;
conf.timestamps = c->timestamps;
if (c->get_timestamp.callback)
{
conf.get_timestamp
= [cb = c->get_timestamp](int64_t msg) { return cb.callback(cb.context, msg); };
}
if (c->on_midi2_message.callback)
conf.on_message = [cb = c->on_midi2_message](const libremidi::ump& msg) {
cb.callback(cb.context, msg.timestamp, msg.data, msg.size());
};
else if (c->on_midi2_raw_data.callback)
{
conf.on_raw_data = [cb = c->on_midi2_raw_data](std::span<const uint32_t> msg, int64_t ts) {
cb.callback(cb.context, ts, msg.data(), msg.size());
};
}
else
{
return -EINVAL;
}
try
{
auto ptr = new libremidi_midi_in_handle{
libremidi::midi_in{std::move(conf), std::move(api_conf)}};
*out = ptr;
}
catch (...)
{
return -EINVAL;
}
break;
}
default:
return -EINVAL;
}
// Open the port
auto ptr = *out;
if (!ptr)
return -EINVAL;
if (c->virtual_port)
{
if (auto ret = ptr->self.open_virtual_port(c->port_name); ret != stdx::error{})
{
*out = nullptr;
delete ptr;
return -EIO;
}
}
else
{
auto port = reinterpret_cast<const libremidi::input_port*>(c->in_port);
if (auto ret = ptr->self.open_port(*port); ret != stdx::error{})
{
*out = nullptr;
delete ptr;
return -EIO;
}
}
return 0;
}
int libremidi_midi_in_is_connected(const libremidi_midi_in_handle* in)
{
if (!in)
return -EINVAL;
return in->self.is_port_connected() ? 1 : 0;
}
libremidi_timestamp libremidi_midi_in_absolute_timestamp(libremidi_midi_in_handle* in)
{
if (!in)
return -EINVAL;
return in->self.absolute_timestamp();
}
int libremidi_midi_in_free(libremidi_midi_in_handle* ptr)
{
delete ptr;
return 0;
}
int libremidi_midi_out_new(
const libremidi_midi_configuration* c, const libremidi_api_configuration* api,
libremidi_midi_out_handle** out)
{
if (!out || !c)
return -EINVAL;
if (!c->virtual_port && !c->out_port)
return -EINVAL;
*out = nullptr;
auto api_conf = libremidi::midi_out_configuration_for(static_cast<libremidi::API>(api->api));
// Create the MIDI object
libremidi::output_configuration conf;
libremidi::assign_error_callback(c->on_error, conf.on_error);
libremidi::assign_error_callback(c->on_warning, conf.on_warning);
conf.timestamps = c->timestamps;
try
{
auto ptr
= new libremidi_midi_out_handle{libremidi::midi_out{std::move(conf), std::move(api_conf)}};
*out = ptr;
}
catch (...)
{
return -EINVAL;
}
// Open the port
auto ptr = *out;
if (!ptr)
return -EINVAL;
if (c->virtual_port)
{
if (auto ret = ptr->self.open_virtual_port(c->port_name); ret != stdx::error{})
{
*out = nullptr;
delete ptr;
return -EIO;
}
}
else
{
auto port = reinterpret_cast<const libremidi::output_port*>(c->in_port);
if (auto ret = ptr->self.open_port(*port); ret != stdx::error{})
{
*out = nullptr;
delete ptr;
return -EIO;
}
}
return 0;
}
int libremidi_midi_out_is_connected(const libremidi_midi_out_handle* out)
{
if (!out)
return -EINVAL;
return out->self.is_port_connected() ? 1 : 0;
}
int libremidi_midi_out_send_message(
libremidi_midi_out_handle* out, const libremidi_midi1_symbol* msg, size_t sz)
{
if (!out || !msg || sz > std::numeric_limits<int32_t>::max())
return -EINVAL;
auto res = out->self.send_message(msg, sz);
return res != stdx::error{} ? -EIO : 0;
}
int libremidi_midi_out_send_ump(libremidi_midi_out_handle* out, const libremidi_midi2_symbol* msg, size_t sz)
{
if (!out || !msg || sz > std::numeric_limits<int32_t>::max())
return -EINVAL;
auto res = out->self.send_ump(msg, sz);
return res != stdx::error{} ? -EIO : 0;
}
int libremidi_midi_out_schedule_message(
libremidi_midi_out_handle* out, int64_t ts, const libremidi_midi1_symbol* msg, size_t sz)
{
if (!out || !msg || sz > std::numeric_limits<int32_t>::max())
return -EINVAL;
auto res = out->self.schedule_message(ts, msg, sz);
return res != stdx::error{} ? -EIO : 0;
}
int libremidi_midi_out_schedule_ump(
libremidi_midi_out_handle* out, int64_t ts, const libremidi_midi2_symbol* msg, size_t sz)
{
if (!out || !msg || sz > std::numeric_limits<int32_t>::max())
return -EINVAL;
auto res = out->self.schedule_ump(ts, msg, sz);
return res != stdx::error{} ? -EIO : 0;
}
int libremidi_midi_out_free(libremidi_midi_out_handle* ptr)
{
delete ptr;
return 0;
}
}