// clang-format off #include #include #include // clang-format on #include #include #include #include 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 { 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_midi1_available_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_midi2_available_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(port)}; *dst = reinterpret_cast(copied); return 0; } int libremidi_midi_in_port_free(libremidi_midi_in_port* port) { delete reinterpret_cast(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(port); *name = p.port_name.data(); *len = p.port_name.size(); return 0; } int libremidi_midi_in_port_handle(const libremidi_midi_in_port* port, uint64_t* handle) { if (!port || !handle) return -EINVAL; auto& p = *reinterpret_cast(port); *handle = static_cast(p.port); 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(port)}; *dst = reinterpret_cast(copied); return 0; } int libremidi_midi_out_port_free(libremidi_midi_out_port* port) { delete reinterpret_cast(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(port); *name = p.port_name.data(); *len = p.port_name.size(); return 0; } int libremidi_midi_out_port_handle(const libremidi_midi_out_port* port, uint64_t* handle) { if (!port || !handle) return -EINVAL; auto& p = *reinterpret_cast(port); *handle = static_cast(p.port); 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(api->api)); if (c->input_added.callback) { conf.input_added = [cb = c->input_added](const auto& port) { cb.callback(cb.context, reinterpret_cast(&port)); }; } if (c->input_removed.callback) { conf.input_removed = [cb = c->input_removed](const auto& port) { cb.callback(cb.context, reinterpret_cast(&port)); }; } if (c->output_added.callback) { conf.output_added = [cb = c->output_added](const auto& port) { cb.callback(cb.context, reinterpret_cast(&port)); }; } if (c->output_removed.callback) { conf.output_removed = [cb = c->output_removed](const auto& port) { cb.callback(cb.context, reinterpret_cast(&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(&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(&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(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->version == libremidi_midi_configuration::MIDI1 && 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->version == libremidi_midi_configuration::MIDI1_RAW && c->on_midi1_raw_data.callback) { conf.on_raw_data = [cb = c->on_midi1_raw_data](std::span 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->version == libremidi_midi_configuration::MIDI2 && 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->version == libremidi_midi_configuration::MIDI2_RAW && c->on_midi2_raw_data.callback) { conf.on_raw_data = [cb = c->on_midi2_raw_data](std::span 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(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(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(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::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::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::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::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; } }