libremidi 0.2.0 → 0.3.0
raw patch · 20 files changed
+2867/−2825 lines, 20 files
Files
- libremidi.cabal +16/−13
- libremidi/client.cpp +0/−47
- libremidi/libremidi-c.cpp +0/−507
- libremidi/libremidi-c.h +0/−275
- libremidi/libremidi.cpp +0/−186
- libremidi/midi_in.cpp +0/−373
- libremidi/midi_out.cpp +0/−326
- libremidi/observer.cpp +0/−133
- libremidi/reader.cpp +0/−752
- libremidi/writer.cpp +0/−213
- vendor/libremidi/api-c.h +39/−0
- vendor/libremidi/client.cpp +47/−0
- vendor/libremidi/libremidi-c.cpp +507/−0
- vendor/libremidi/libremidi-c.h +275/−0
- vendor/libremidi/libremidi.cpp +186/−0
- vendor/libremidi/midi_in.cpp +373/−0
- vendor/libremidi/midi_out.cpp +326/−0
- vendor/libremidi/observer.cpp +133/−0
- vendor/libremidi/reader.cpp +752/−0
- vendor/libremidi/writer.cpp +213/−0
libremidi.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: libremidi-version: 0.2.0+version: 0.3.0 synopsis: libremidi bindings for haskell description: Please see the README on GitHub at <https://github.com/ejconlon/libremidi-haskell#readme> homepage: https://github.com/ejconlon/libremidi-haskell#readme@@ -65,18 +65,19 @@ TypeFamilies ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds include-dirs:- ./+ vendor install-includes:- libremidi/libremidi-c.h+ vendor/libremidi/api-c.h+ vendor/libremidi/libremidi-c.h cxx-sources:- libremidi/client.cpp- libremidi/libremidi-c.cpp- libremidi/libremidi.cpp- libremidi/midi_in.cpp- libremidi/midi_out.cpp- libremidi/observer.cpp- libremidi/reader.cpp- libremidi/writer.cpp+ vendor/libremidi/client.cpp+ vendor/libremidi/libremidi-c.cpp+ vendor/libremidi/libremidi.cpp+ vendor/libremidi/midi_in.cpp+ vendor/libremidi/midi_out.cpp+ vendor/libremidi/observer.cpp+ vendor/libremidi/reader.cpp+ vendor/libremidi/writer.cpp extra-libraries: stdc++ build-depends:@@ -140,7 +141,8 @@ TypeFamilies ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds -threaded -rtsopts -with-rtsopts=-N install-includes:- libremidi/libremidi-c.h+ vendor/libremidi/api-c.h+ vendor/libremidi/libremidi-c.h build-depends: base >=4.12 && <5 , containers >=0.6.7 && <0.7@@ -192,7 +194,8 @@ TypeFamilies ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds -threaded -rtsopts -with-rtsopts=-N install-includes:- libremidi/libremidi-c.h+ vendor/libremidi/api-c.h+ vendor/libremidi/libremidi-c.h build-depends: base >=4.12 && <5 , containers >=0.6.7 && <0.7
− libremidi/client.cpp
@@ -1,47 +0,0 @@-#if !defined(LIBREMIDI_HEADER_ONLY)- #include "client.hpp"-#endif-#include <libremidi/backends.hpp>-#include <libremidi/shared_context.hpp>--#ifdef LIBREMIDI_ALSA- #include <libremidi/backends/alsa_seq/shared_handler.hpp>-#endif-#ifdef LIBREMIDI_JACK- #include <libremidi/backends/jack/shared_handler.hpp>-#endif-namespace libremidi-{-LIBREMIDI_INLINE-shared_configurations-create_shared_context(const libremidi::API api, [[maybe_unused]] std::string_view client_name)-{- switch (api)- {-#if __has_include(<boost/lockfree/spsc_queue.hpp>)- #if defined(LIBREMIDI_ALSA)- case libremidi::API::ALSA_SEQ:- return alsa_seq::shared_handler::make(client_name);- #endif-- #if defined(LIBREMIDI_JACK)- case libremidi::API::JACK_MIDI:- return jack::shared_handler::make(client_name);- #endif-#endif- case libremidi::API::COREMIDI:- case libremidi::API::COREMIDI_UMP:- // TODO-- case libremidi::API::WINDOWS_MIDI_SERVICES:- // TODO-- default:- return {- .context = {},- .observer = observer_configuration_for(api),- .in = midi_in_configuration_for(api),- .out = midi_out_configuration_for(api)};- }-}-}
− libremidi/libremidi-c.cpp
@@ -1,507 +0,0 @@-// 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;-}-}
− libremidi/libremidi-c.h
@@ -1,275 +0,0 @@-#pragma once-#include <libremidi/api-c.h>--#include <stdbool.h>-#include <stddef.h>-#include <stdint.h>--#if defined(LIBREMIDI_EXPORTS)- #if defined(_MSC_VER)- #define LIBREMIDI_EXPORT __declspec(dllexport)- #elif defined(__GNUC__) || defined(__clang__)- #define LIBREMIDI_EXPORT __attribute__((visibility("default")))- #endif-#else- #define LIBREMIDI_EXPORT-#endif--#if __cplusplus-extern "C" {-#endif-typedef unsigned char libremidi_midi1_symbol;-typedef libremidi_midi1_symbol* libremidi_midi1_message;--typedef uint32_t libremidi_midi2_symbol;-typedef libremidi_midi2_symbol* libremidi_midi2_message;--typedef int64_t libremidi_timestamp;--typedef struct libremidi_midi_in_port libremidi_midi_in_port;-typedef struct libremidi_midi_out_port libremidi_midi_out_port;-typedef struct libremidi_midi_in_handle libremidi_midi_in_handle;-typedef struct libremidi_midi_out_handle libremidi_midi_out_handle;-typedef struct libremidi_midi_observer_handle libremidi_midi_observer_handle;--typedef struct libremidi_api_configuration libremidi_api_configuration;--enum libremidi_timestamp_mode-{- NoTimestamp,-- Relative,- Absolute,- SystemMonotonic,- AudioFrame,- Custom-};--typedef struct libremidi_api_configuration-{- enum libremidi_api api;- enum- {- Observer,- Input,- Output- } configuration_type;-- void* data;-} libremidi_api_configuration;--typedef struct libremidi_observer_configuration-{- struct- {- void* context;- void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);- } on_error;- struct- {- void* context;- void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);- } on_warning;-- struct- {- void* context;- void (*callback)(void* ctx, const libremidi_midi_in_port*);- } input_added;- struct- {- void* context;- void (*callback)(void* ctx, const libremidi_midi_in_port*);- } input_removed;- struct- {- void* context;- void (*callback)(void* ctx, const libremidi_midi_out_port*);- } output_added;- struct- {- void* context;- void (*callback)(void* ctx, const libremidi_midi_out_port*);- } output_removed;-- bool track_hardware;- bool track_virtual;- bool track_any;- bool notify_in_constructor;-} libremidi_observer_configuration;--typedef struct libremidi_midi1_callback-{- void* context;- void (*callback)(void* ctx, libremidi_timestamp, const libremidi_midi1_symbol*, size_t len);-} libremidi_midi1_callback;--typedef struct libremidi_midi2_callback-{- void* context;- void (*callback)(void* ctx, libremidi_timestamp, const libremidi_midi2_symbol*, size_t len);-} libremidi_midi2_callback;--typedef struct libremidi_midi_configuration-{- // Indicates the kind of callback requested, e.g. set MIDI1_RAW if you use the on_midi1_raw_data- enum- {- MIDI1 = (1 << 1),- MIDI1_RAW = (1 << 2),- MIDI2 = (1 << 3),- MIDI2_RAW = (1 << 4)- } version;-- union- {- libremidi_midi_in_port* in_port;- libremidi_midi_out_port* out_port;- };-- union- {- libremidi_midi1_callback on_midi1_message;- libremidi_midi1_callback on_midi1_raw_data;- libremidi_midi2_callback on_midi2_message;- libremidi_midi2_callback on_midi2_raw_data;- };-- struct- {- void* context;- libremidi_timestamp (*callback)(void* ctx, libremidi_timestamp);- } get_timestamp;-- struct- {- void* context;- void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);- } on_error;- struct- {- void* context;- void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);- } on_warning;-- const char* port_name;- bool virtual_port;-- bool ignore_sysex;- bool ignore_timing;- bool ignore_sensing;-- enum libremidi_timestamp_mode timestamps;-} libremidi_midi_configuration;--/// API utilities-LIBREMIDI_EXPORT-const char* libremidi_get_version(void);--LIBREMIDI_EXPORT-void libremidi_midi1_available_apis(void* ctx, void (*)(void*, libremidi_api));-LIBREMIDI_EXPORT-void libremidi_midi2_available_apis(void* ctx, void (*)(void*, libremidi_api));-LIBREMIDI_EXPORT-const char* libremidi_api_identifier(libremidi_api);-LIBREMIDI_EXPORT-const char* libremidi_api_display_name(libremidi_api);-LIBREMIDI_EXPORT-libremidi_api libremidi_get_compiled_api_by_identifier(const char*);--/// Create configurations-LIBREMIDI_EXPORT-int libremidi_midi_api_configuration_init(libremidi_api_configuration*);--LIBREMIDI_EXPORT-int libremidi_midi_observer_configuration_init(libremidi_observer_configuration*);--LIBREMIDI_EXPORT-int libremidi_midi_configuration_init(libremidi_midi_configuration*);--/// Read information about port objects-LIBREMIDI_EXPORT-int libremidi_midi_in_port_clone(const libremidi_midi_in_port* port, libremidi_midi_in_port** dst);--LIBREMIDI_EXPORT-int libremidi_midi_in_port_free(libremidi_midi_in_port* port);--LIBREMIDI_EXPORT-int libremidi_midi_in_port_name(- const libremidi_midi_in_port* port, const char** name, size_t* len);--LIBREMIDI_EXPORT-int libremidi_midi_out_port_clone(- const libremidi_midi_out_port* port, libremidi_midi_out_port** dst);--LIBREMIDI_EXPORT-int libremidi_midi_out_port_free(libremidi_midi_out_port* port);--LIBREMIDI_EXPORT-int libremidi_midi_out_port_name(- const libremidi_midi_out_port* port, const char** name, size_t* len);--/// Observer API-LIBREMIDI_EXPORT-int libremidi_midi_observer_new(- const libremidi_observer_configuration*, libremidi_api_configuration*,- libremidi_midi_observer_handle**);--LIBREMIDI_EXPORT-int libremidi_midi_observer_enumerate_input_ports(- libremidi_midi_observer_handle*, void* context,- void (*)(void* ctx, const libremidi_midi_in_port*));--LIBREMIDI_EXPORT-int libremidi_midi_observer_enumerate_output_ports(- libremidi_midi_observer_handle*, void* context,- void (*)(void* ctx, const libremidi_midi_out_port*));--LIBREMIDI_EXPORT-int libremidi_midi_observer_free(libremidi_midi_observer_handle*);--/// MIDI input API (read MIDI messages)-LIBREMIDI_EXPORT-int libremidi_midi_in_new(- const libremidi_midi_configuration*, const libremidi_api_configuration*,- libremidi_midi_in_handle**);--LIBREMIDI_EXPORT-int libremidi_midi_in_is_connected(const libremidi_midi_in_handle*);--LIBREMIDI_EXPORT-libremidi_timestamp libremidi_midi_in_absolute_timestamp(libremidi_midi_in_handle*);--LIBREMIDI_EXPORT-int libremidi_midi_in_free(libremidi_midi_in_handle*);--/// MIDI output API (send MIDI messages)-LIBREMIDI_EXPORT-int libremidi_midi_out_new(- const libremidi_midi_configuration*, const libremidi_api_configuration*,- libremidi_midi_out_handle**);--LIBREMIDI_EXPORT-int libremidi_midi_out_is_connected(const libremidi_midi_out_handle*);--LIBREMIDI_EXPORT-int libremidi_midi_out_send_message(- libremidi_midi_out_handle*, const libremidi_midi1_symbol*, size_t);--LIBREMIDI_EXPORT-int libremidi_midi_out_send_ump(libremidi_midi_out_handle*, const libremidi_midi2_symbol*, size_t);--LIBREMIDI_EXPORT-int libremidi_midi_out_schedule_message(- libremidi_midi_out_handle*, int64_t ts, const libremidi_midi1_symbol*, size_t);--LIBREMIDI_EXPORT-int libremidi_midi_out_schedule_ump(- libremidi_midi_out_handle*, int64_t ts, const libremidi_midi2_symbol*, size_t);--LIBREMIDI_EXPORT-int libremidi_midi_out_free(libremidi_midi_out_handle*);--#if __cplusplus-}-#endif
− libremidi/libremidi.cpp
@@ -1,186 +0,0 @@-#if !defined(LIBREMIDI_HEADER_ONLY)- #include <libremidi/libremidi.hpp>-#endif--#include <libremidi/backends.hpp>-#include <libremidi/detail/midi_api.hpp>--#include <cmath>--namespace libremidi-{--LIBREMIDI_INLINE-std::string_view get_version() noexcept-{- return LIBREMIDI_VERSION;-}--LIBREMIDI_INLINE std::string_view get_api_name(libremidi::API api)-{- std::string_view ret;- midi_any::for_backend(api, [&](auto& b) { ret = b.name; });- return ret;-}--LIBREMIDI_INLINE std::string_view get_api_display_name(libremidi::API api)-{- std::string_view ret;- midi_any::for_backend(api, [&](auto& b) { ret = b.display_name; });- return ret;-}--LIBREMIDI_INLINE libremidi::API get_compiled_api_by_name(std::string_view name)-{- libremidi::API ret = libremidi::API::UNSPECIFIED;- midi_any::for_all_backends([&](auto& b) {- if (name == b.name)- ret = b.API;- });- return ret;-}--[[nodiscard]] LIBREMIDI_INLINE std::vector<libremidi::API> available_apis() noexcept-{- std::vector<libremidi::API> apis;- midi1::for_all_backends([&](auto b) { apis.push_back(b.API); });- return apis;-}--[[nodiscard]] LIBREMIDI_INLINE std::vector<libremidi::API> available_ump_apis() noexcept-{- std::vector<libremidi::API> apis;- midi2::for_all_backends([&](auto b) { apis.push_back(b.API); });- return apis;-}-LIBREMIDI_INLINE-libremidi::API midi_api(const std::any& conf)-{- libremidi::API ret = libremidi::API::UNSPECIFIED;- midi_any::for_all_backends([&]<typename T>(T) {- if (std::any_cast<typename T::midi_in_configuration>(&conf)- || std::any_cast<typename T::midi_out_configuration>(&conf)- || std::any_cast<typename T::midi_observer_configuration>(&conf))- {- ret = T::API;- }- });- return ret;-}-LIBREMIDI_INLINE-std::any midi_in_configuration_for(libremidi::API api)-{- std::any ret;- midi_any::for_backend(api, [&]<typename T>(T) {- using conf_type = typename T::midi_in_configuration;- ret = conf_type{};- });- return ret;-}--LIBREMIDI_INLINE-std::any midi_out_configuration_for(libremidi::API api)-{- std::any ret;- midi_any::for_backend(api, [&]<typename T>(T) {- using conf_type = typename T::midi_out_configuration;- ret = conf_type{};- });- return ret;-}--LIBREMIDI_INLINE-std::any observer_configuration_for(libremidi::API api)-{- std::any ret;- midi_any::for_backend(api, [&]<typename T>(T) {- using conf_type = typename T::midi_observer_configuration;- ret = conf_type{};- });- return ret;-}--LIBREMIDI_INLINE-std::any midi_in_configuration_for(const libremidi::observer& obs)-{- return midi_in_configuration_for(obs.get_current_api());-}--LIBREMIDI_INLINE-std::any midi_out_configuration_for(const libremidi::observer& obs)-{- // FIXME reuse context when meaningful, e.g. ALSA, JACK...- return midi_out_configuration_for(obs.get_current_api());-}--LIBREMIDI_INLINE-std::optional<input_port> in_default_port(libremidi::API api) noexcept-try-{- libremidi::observer obs{{}, observer_configuration_for(api)};- if (auto ports = obs.get_input_ports(); !ports.empty())- return ports.front();- return std::nullopt;-}-catch (const std::exception& e)-{- return std::nullopt;-}--LIBREMIDI_INLINE-std::optional<output_port> out_default_port(libremidi::API api) noexcept-try-{- libremidi::observer obs{{}, observer_configuration_for(api)};- if (auto ports = obs.get_output_ports(); !ports.empty())- return ports.front();- return std::nullopt;-}-catch (const std::exception& e)-{- return std::nullopt;-}--namespace midi1-{-LIBREMIDI_INLINE-std::any in_default_configuration()-{- return midi_in_configuration_for(default_api());-}--LIBREMIDI_INLINE-std::any out_default_configuration()-{- return midi_out_configuration_for(default_api());-}--LIBREMIDI_INLINE-std::any observer_default_configuration()-{- return observer_configuration_for(default_api());-}-}--namespace midi2-{-LIBREMIDI_INLINE-std::any in_default_configuration()-{- return midi_in_configuration_for(default_api());-}--LIBREMIDI_INLINE-std::any out_default_configuration()-{- return midi_out_configuration_for(default_api());-}--LIBREMIDI_INLINE-std::any observer_default_configuration()-{- return observer_configuration_for(default_api());-}-}--}
− libremidi/midi_in.cpp
@@ -1,373 +0,0 @@-#if !defined(LIBREMIDI_HEADER_ONLY)- #include <libremidi/libremidi.hpp>-#endif--#include <libremidi/backends.hpp>-#include <libremidi/detail/midi_api.hpp>--#include <cassert>--namespace libremidi-{--static libremidi::ump_input_configuration-convert_midi1_to_midi2_input_configuration(const input_configuration& base_conf) noexcept-{- libremidi::ump_input_configuration c2;- c2.on_message = [cb = base_conf.on_message,- converter = midi2_to_midi1{}](libremidi::ump&& msg) mutable -> void {- converter.convert(- msg.data, 1, msg.timestamp, [cb](const unsigned char* midi, std::size_t n, int64_t ts) {- cb(libremidi::message{{midi, midi + n}, ts});- return stdx::error{};- });- };- c2.get_timestamp = base_conf.get_timestamp;- c2.on_error = base_conf.on_error;- c2.on_warning = base_conf.on_warning;- c2.ignore_sysex = base_conf.ignore_sysex;- c2.ignore_timing = base_conf.ignore_timing;- c2.ignore_sensing = base_conf.ignore_sensing;- c2.timestamps = base_conf.timestamps;- return c2;-}--static libremidi::input_configuration-convert_midi2_to_midi1_input_configuration(const ump_input_configuration& base_conf) noexcept-{- libremidi::input_configuration c2;- c2.on_message = [cb = base_conf.on_message,- converter = midi1_to_midi2{}](libremidi::message&& msg) mutable -> void {- converter.convert(- msg.bytes.data(), msg.bytes.size(), msg.timestamp,- [cb](const uint32_t* ump, std::size_t n, int64_t ts) {- if(n >= 4)- {- libremidi::ump u{ump[0],ump[1],ump[2],ump[3]};- u.timestamp = ts;- cb(std::move(u));- }- return stdx::error{};- });- };- c2.get_timestamp = base_conf.get_timestamp;- c2.on_error = base_conf.on_error;- c2.on_warning = base_conf.on_warning;- c2.ignore_sysex = base_conf.ignore_sysex;- c2.ignore_timing = base_conf.ignore_timing;- c2.ignore_sensing = base_conf.ignore_sensing;- c2.timestamps = base_conf.timestamps;- return c2;-}--static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>-make_midi_in(auto base_conf, std::any api_conf, auto backends)-{- std::unique_ptr<midi_in_api> ptr;-- assert(base_conf.on_message || base_conf.on_raw_data);-- auto from_api = [&]<typename T>(T& /*backend*/) mutable {- if (auto conf = std::any_cast<typename T::midi_in_configuration>(&api_conf))- {- ptr = libremidi::make<typename T::midi_in>(std::move(base_conf), std::move(*conf));- return true;- }- return false;- };- std::apply([&](auto&&... b) { (from_api(b) || ...); }, backends);- return ptr;-}--/// MIDI 1 helpers-static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>-make_midi1_in(const input_configuration& base_conf)-{- for (const auto& api : available_apis())- {- try- {- auto impl- = make_midi_in(base_conf, midi_in_configuration_for(api), midi1::available_backends);- if (impl)- return impl;- }- catch (const std::exception& e)- {- }- }-- // No MIDI 1 backend, try the MIDI 2 ones with a wrap:- {- auto c2 = convert_midi1_to_midi2_input_configuration(base_conf);- for (const auto& api : available_ump_apis())- {- try- {- auto impl = make_midi_in(c2, midi_in_configuration_for(api), midi2::available_backends);- if (impl)- return impl;- }- catch (const std::exception& e)- {- }- }- }-- return std::make_unique<midi_in_dummy>(input_configuration{}, dummy_configuration{});-}--static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>-make_midi1_in(const input_configuration& base_conf, const std::any& api_conf, libremidi::API api)-{- if (libremidi::is_midi1(api))- {- return make_midi_in(base_conf, api_conf, midi1::available_backends);- }- else if (libremidi::is_midi2(api))- {- auto c2 = convert_midi1_to_midi2_input_configuration(base_conf);- return make_midi_in(c2, api_conf, midi2::available_backends);- }- return {};-}--static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>-make_midi1_in(const input_configuration& base_conf, const std::any& api_conf)-{- if (!api_conf.has_value())- {- return make_midi1_in(base_conf);- }- else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))- {- if (*api_p == libremidi::API::UNSPECIFIED)- {- return make_midi1_in(base_conf);- }- else- {- return make_midi1_in(base_conf, midi_in_configuration_for(*api_p), *api_p);- }- }- else- {- if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)- return {};- else- return make_midi1_in(base_conf, api_conf, libremidi::midi_api(api_conf));- }-}--/// MIDI 1 constructors-LIBREMIDI_INLINE midi_in::midi_in(const input_configuration& base_conf) noexcept- : impl_{make_midi1_in(base_conf)}-{-}--LIBREMIDI_INLINE-midi_in::midi_in(input_configuration base_conf, std::any api_conf)- : impl_{make_midi1_in(base_conf, api_conf)}-{- if (!impl_)- {- error_handler e;- e.libremidi_handle_error(base_conf, "Could not open midi in for the given api");- impl_ = std::make_unique<midi_in_dummy>(input_configuration{}, dummy_configuration{});- }-}--/// MIDI 2 helpers-static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>-make_midi2_in(const ump_input_configuration& base_conf)-{- for (const auto& api : available_ump_apis())- {- try- {- if (auto ret- = make_midi_in(base_conf, midi_in_configuration_for(api), midi2::available_backends))- return ret;- }- catch (const std::exception& e)- {- }- }-- // No MIDI 2 backend, try the MIDI 1 ones with a wrap:- {- auto c2 = convert_midi2_to_midi1_input_configuration(base_conf);- for (const auto& api : available_apis())- {- try- {- if (auto ret = make_midi_in(c2, midi_in_configuration_for(api), midi1::available_backends))- return ret;- }- catch (const std::exception& e)- {- }- }- }-- return {};-}--static LIBREMIDI_INLINE std::unique_ptr<midi_in_api> make_midi2_in(- const ump_input_configuration& base_conf, const std::any& api_conf, libremidi::API api)-{- if (is_midi2(api))- {- return make_midi_in(base_conf, api_conf, midi2::available_backends);- }- else if (is_midi1(api))- {- auto c2 = convert_midi2_to_midi1_input_configuration(base_conf);- return make_midi_in(c2, api_conf, midi1::available_backends);- }-- return {};-}--static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>-make_midi2_in(const ump_input_configuration& base_conf, const std::any& api_conf)-{- if (!api_conf.has_value())- {- return make_midi2_in(base_conf);- }- else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))- {- if (*api_p == libremidi::API::UNSPECIFIED)- {- return make_midi2_in(base_conf);- }- else- {- return make_midi2_in(base_conf, midi_in_configuration_for(*api_p), *api_p);- }- }- else- {- if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)- return {};- else- return make_midi2_in(base_conf, api_conf, libremidi::midi_api(api_conf));- }-}--/// MIDI 2 constructors-LIBREMIDI_INLINE midi_in::midi_in(ump_input_configuration base_conf) noexcept- : impl_{make_midi2_in(base_conf)}-{-}--LIBREMIDI_INLINE-midi_in::midi_in(ump_input_configuration base_conf, std::any api_conf)- : impl_{make_midi2_in(base_conf, api_conf)}-{- if (!impl_)- {- error_handler e;- e.libremidi_handle_error(base_conf, "Could not open midi in for the given api");- impl_ = std::make_unique<midi_in_dummy>(input_configuration{}, dummy_configuration{});- }-}--LIBREMIDI_INLINE midi_in::~midi_in() = default;--LIBREMIDI_INLINE midi_in::midi_in(midi_in&& other) noexcept- : impl_{std::move(other.impl_)}-{- other.impl_- = std::make_unique<libremidi::midi_in_dummy>(input_configuration{}, dummy_configuration{});-}--LIBREMIDI_INLINE-stdx::error midi_in::set_port_name(std::string_view portName)-{- if(impl_->is_port_open())- return impl_->set_port_name(portName);-- return std::errc::not_connected;-}--LIBREMIDI_INLINE midi_in& midi_in::operator=(midi_in&& other) noexcept-{- this->impl_ = std::move(other.impl_);- other.impl_- = std::make_unique<libremidi::midi_in_dummy>(input_configuration{}, dummy_configuration{});- return *this;-}--LIBREMIDI_INLINE-libremidi::API midi_in::get_current_api() const noexcept-{- return impl_->get_current_api();-}--LIBREMIDI_INLINE-stdx::error midi_in::open_port(const input_port& port, std::string_view portName)-{- if (auto err = impl_->is_client_open(); err != stdx::error{})- return std::errc::not_connected;-- if (impl_->is_port_open())- return std::errc::operation_not_supported;-- auto ret = impl_->open_port(port, portName);- if (ret == stdx::error{})- {- impl_->connected_ = true;- impl_->port_open_ = true;- }- return ret;-}--LIBREMIDI_INLINE-stdx::error midi_in::open_virtual_port(std::string_view portName)-{- if (auto err = impl_->is_client_open(); err != stdx::error{})- return std::errc::not_connected;-- if (impl_->is_port_open())- return std::errc::operation_not_supported;-- auto ret = impl_->open_virtual_port(portName);- if (ret == stdx::error{})- impl_->port_open_ = true;- return ret;-}--LIBREMIDI_INLINE-stdx::error midi_in::close_port()-{- if (auto err = impl_->is_client_open(); err != stdx::error{})- return std::errc::not_connected;-- auto ret = impl_->close_port();-- impl_->connected_ = false;- impl_->port_open_ = false;-- return ret;-}--LIBREMIDI_INLINE-bool midi_in::is_port_open() const noexcept-{- return impl_->is_port_open();-}--LIBREMIDI_INLINE-bool midi_in::is_port_connected() const noexcept-{- return impl_->is_port_connected();-}--LIBREMIDI_INLINE-int64_t midi_in::absolute_timestamp() const noexcept-{- return impl_->absolute_timestamp();-}-}
− libremidi/midi_out.cpp
@@ -1,326 +0,0 @@-#if !defined(LIBREMIDI_HEADER_ONLY)- #include <libremidi/libremidi.hpp>-#endif--#include <libremidi/backends.hpp>-#include <libremidi/detail/midi_api.hpp>--#include <array>-#include <cassert>--namespace libremidi-{-static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>-make_midi_out_impl(auto base_conf, std::any api_conf)-{- std::unique_ptr<midi_out_api> ptr;- auto from_api = [&]<typename T>(T& /*backend*/) mutable {- if (auto conf = std::any_cast<typename T::midi_out_configuration>(&api_conf))- {- ptr = libremidi::make<typename T::midi_out>(std::move(base_conf), std::move(*conf));- return true;- }- return false;- };- std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi1::available_backends);- std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi2::available_backends);- return ptr;-}--static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>-make_midi_out(const output_configuration& base_conf)-{- for (const auto& api : available_apis())- {- try- {- if (auto ret = make_midi_out_impl(base_conf, midi_out_configuration_for(api)))- return ret;- }- catch (const std::exception& e)- {- }- }-- for (const auto& api : available_ump_apis())- {- try- {- if (auto ret = make_midi_out_impl(base_conf, midi_out_configuration_for(api)))- return ret;- }- catch (const std::exception& e)- {- }- }-- return std::make_unique<midi_out_dummy>(output_configuration{}, dummy_configuration{});-}--static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>-make_midi_out(const output_configuration& base_conf, const std::any& api_conf)-{- if (!api_conf.has_value())- {- return make_midi_out(base_conf);- }- else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))- {- if (*api_p == libremidi::API::UNSPECIFIED)- {- return make_midi_out(base_conf);- }- else- {- return make_midi_out_impl(base_conf, midi_out_configuration_for(*api_p));- }- }- else- {- if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)- return make_midi_out(base_conf);- else- return make_midi_out_impl(base_conf, api_conf);- }-}--LIBREMIDI_INLINE midi_out::midi_out(const output_configuration& base_conf) noexcept- : impl_{make_midi_out(base_conf)}-{-}--LIBREMIDI_INLINE-midi_out::midi_out(output_configuration base_conf, std::any api_conf)- : impl_{make_midi_out(base_conf, api_conf)}-{- if (!impl_)- {- error_handler e;- e.libremidi_handle_error(base_conf, "Could not open midi out for the given api");- impl_ = std::make_unique<midi_out_dummy>(output_configuration{}, dummy_configuration{});- }-}--LIBREMIDI_INLINE midi_out::~midi_out() = default;--LIBREMIDI_INLINE midi_out::midi_out(midi_out&& other) noexcept- : impl_{std::move(other.impl_)}-{- other.impl_- = std::make_unique<libremidi::midi_out_dummy>(output_configuration{}, dummy_configuration{});-}--LIBREMIDI_INLINE midi_out& midi_out::operator=(midi_out&& other) noexcept-{- this->impl_ = std::move(other.impl_);- other.impl_- = std::make_unique<libremidi::midi_out_dummy>(output_configuration{}, dummy_configuration{});- return *this;-}--LIBREMIDI_INLINE-stdx::error midi_out::set_port_name(std::string_view portName) const-{- if(impl_->is_port_open())- return impl_->set_port_name(portName);-- return std::errc::not_connected;-}--LIBREMIDI_INLINE-libremidi::API midi_out::get_current_api() const noexcept-{- return impl_->get_current_api();-}--LIBREMIDI_INLINE-stdx::error midi_out::open_port(const output_port& port, std::string_view portName) const-{- if (auto err = impl_->is_client_open(); err != stdx::error{})- return std::errc::not_connected;-- if (impl_->is_port_open())- return std::errc::operation_not_supported;-- auto ret = impl_->open_port(port, portName);- if (ret == stdx::error{})- {- impl_->connected_ = true;- impl_->port_open_ = true;- }- return ret;-}--LIBREMIDI_INLINE-stdx::error midi_out::open_virtual_port(std::string_view portName) const-{- if (auto err = impl_->is_client_open(); err != stdx::error{})- return std::errc::not_connected;-- if (impl_->is_port_open())- return std::errc::operation_not_supported;-- auto ret = impl_->open_virtual_port(portName);- if (ret == stdx::error{})- impl_->port_open_ = true;- return ret;-}--LIBREMIDI_INLINE-stdx::error midi_out::close_port() const-{- if (auto err = impl_->is_client_open(); err != stdx::error{})- return std::errc::not_connected;-- auto ret = impl_->close_port();- impl_->connected_ = false;- impl_->port_open_ = false;- return ret;-}--LIBREMIDI_INLINE-bool midi_out::is_port_open() const noexcept-{- return impl_->is_port_open();-}--LIBREMIDI_INLINE-bool midi_out::is_port_connected() const noexcept-{- return impl_->is_port_connected();-}--LIBREMIDI_INLINE-stdx::error midi_out::send_message(const libremidi::message& message) const-{- return send_message(message.bytes.data(), message.bytes.size());-}--LIBREMIDI_INLINE-stdx::error midi_out::send_message(std::span<const unsigned char> message) const-{- return send_message(message.data(), message.size());-}--LIBREMIDI_INLINE-stdx::error midi_out::send_message(unsigned char b0) const-{- return send_message(&b0, 1);-}--LIBREMIDI_INLINE-stdx::error midi_out::send_message(unsigned char b0, unsigned char b1) const-{- return send_message(std::to_array({b0, b1}));-}--LIBREMIDI_INLINE-stdx::error midi_out::send_message(unsigned char b0, unsigned char b1, unsigned char b2) const-{- return send_message(std::to_array({b0, b1, b2}));-}--LIBREMIDI_INLINE-stdx::error midi_out::send_message(const unsigned char* message, size_t size) const-{-#if defined(LIBREMIDI_ASSERTIONS)- assert(size > 0);-#endif-- return impl_->send_message(message, size);-}--LIBREMIDI_INLINE-int64_t midi_out::current_time()-{- return impl_->current_time();-}--LIBREMIDI_INLINE-stdx::error midi_out::schedule_message(int64_t ts, const unsigned char* message, size_t size) const-{-#if defined(LIBREMIDI_ASSERTIONS)- assert(size > 0);-#endif-- return impl_->schedule_message(ts, message, size);-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(const uint32_t* message, size_t size) const-{-#if defined(LIBREMIDI_ASSERTIONS)- assert(size > 0);- assert(size <= 4);-#endif-- return impl_->send_ump(message, size);-}-LIBREMIDI_INLINE-stdx::error midi_out::send_ump(const libremidi::ump& message) const-{- return send_ump(message.data, message.size());-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(std::span<const uint32_t> message) const-{- return send_ump(message.data(), message.size());-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(uint32_t b0) const-{- return send_ump(&b0, 1);-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(uint32_t b0, uint32_t b1) const-{- return send_ump(std::to_array({b0, b1}));-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(uint32_t b0, uint32_t b1, uint32_t b2) const-{- return send_ump(std::to_array({b0, b1, b2}));-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3) const-{- return send_ump(std::to_array({b0, b1, b2, b3}));-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(int32_t b0) const-{- return send_ump(reinterpret_cast<uint32_t*>(&b0), 1);-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(int64_t b01) const-{- auto ptr = reinterpret_cast<uint32_t*>(&b01);- uint32_t msg[2]{ptr[1], ptr[0]};- return send_ump(msg, 2);-}--LIBREMIDI_INLINE-stdx::error midi_out::send_ump(uint64_t b01) const-{- auto ptr = reinterpret_cast<uint32_t*>(&b01);- uint32_t msg[2]{ptr[1], ptr[0]};- return send_ump(msg, 2);-}--LIBREMIDI_INLINE-stdx::error midi_out::schedule_ump(int64_t ts, const uint32_t* message, size_t size) const-{-#if defined(LIBREMIDI_ASSERTIONS)- assert(size > 0);-#endif-- return impl_->schedule_ump(ts, message, size);-}-}
− libremidi/observer.cpp
@@ -1,133 +0,0 @@-#if !defined(LIBREMIDI_HEADER_ONLY)- #include <libremidi/libremidi.hpp>-#endif--#include <libremidi/backends.hpp>-#include <libremidi/detail/midi_api.hpp>--namespace libremidi-{--static LIBREMIDI_INLINE std::unique_ptr<observer_api> make_observer(auto base_conf)-{- for (const auto& api : available_apis())- {- try- {- if (auto impl_ = make_observer(base_conf, observer_configuration_for(api)))- return impl_;- }- catch (const std::exception& e)- {- }- }-- for (const auto& api : available_ump_apis())- {- try- {- if (auto impl_ = make_observer(base_conf, observer_configuration_for(api)))- return impl_;- }- catch (const std::exception& e)- {- }- }-- return std::make_unique<observer_dummy>(observer_configuration{}, dummy_configuration{});-}--LIBREMIDI_INLINE auto make_observer_impl(auto base_conf, std::any api_conf)-{- std::unique_ptr<observer_api> ptr;- auto from_api = [&]<typename T>(T& /*backend*/) mutable {- if (auto conf = std::any_cast<typename T::midi_observer_configuration>(&api_conf))- {- ptr = libremidi::make<typename T::midi_observer>(std::move(base_conf), std::move(*conf));- return true;- }- return false;- };- std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi1::available_backends);- std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi2::available_backends);- return ptr;-}--LIBREMIDI_INLINE std::unique_ptr<observer_api> make_observer(auto base_conf, std::any api_conf)-{- if (!api_conf.has_value())- {- return make_observer(base_conf);- }- else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))- {- if (*api_p == libremidi::API::UNSPECIFIED)- {- return make_observer(base_conf);- }- else- {- return make_observer_impl(base_conf, observer_configuration_for(*api_p));- }- }- else- {- if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)- return {};- else- return make_observer_impl(base_conf, api_conf);- }-}--LIBREMIDI_INLINE observer::observer(const observer_configuration& base_conf) noexcept- : impl_{make_observer(base_conf)}-{-}--LIBREMIDI_INLINE observer::observer(observer_configuration base_conf, std::any api_conf)- : impl_{make_observer(base_conf, api_conf)}-{- if (!impl_)- {- error_handler e;- e.libremidi_handle_error(base_conf, "Could not open observer for the given api");- impl_ = std::make_unique<observer_dummy>(observer_configuration{}, dummy_configuration{});- }-}--LIBREMIDI_INLINE observer::observer(observer&& other) noexcept- : impl_{std::move(other.impl_)}-{- other.impl_ = std::make_unique<libremidi::observer_dummy>(- observer_configuration{}, dummy_configuration{});-}--LIBREMIDI_INLINE observer& observer::operator=(observer&& other) noexcept-{- this->impl_ = std::move(other.impl_);- other.impl_ = std::make_unique<libremidi::observer_dummy>(- observer_configuration{}, dummy_configuration{});- return *this;-}--LIBREMIDI_INLINE-observer::~observer() = default;--LIBREMIDI_INLINE-libremidi::API observer::get_current_api() const noexcept-{- return impl_->get_current_api();-}--LIBREMIDI_INLINE-std::vector<libremidi::input_port> observer::get_input_ports() const noexcept-{- return impl_->get_input_ports();-}--LIBREMIDI_INLINE-std::vector<libremidi::output_port> observer::get_output_ports() const noexcept-{- return impl_->get_output_ports();-}-}
− libremidi/reader.cpp
@@ -1,752 +0,0 @@-/*-Copyright (c) 2015, Dimitri Diakopoulos All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:--* Redistributions of source code must retain the above copyright notice, this- list of conditions and the following disclaimer.--* Redistributions in binary form must reproduce the above copyright notice,- this list of conditions and the following disclaimer in the documentation- and/or other materials provided with the distribution.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.-*/--#if !defined(LIBREMIDI_HEADER_ONLY)- #include <libremidi/reader.hpp>-#endif-#include <libremidi/message.hpp>--#include <algorithm>-#include <iostream>--// File Parsing Validation Todo:-// ==============================-// [] Bad file name-// [] Bad header-// [] Unknown header type-// [] Bad header size-// [] Bad type-// [] Bad tmecode-// [] Header too short-// [] Track too short-// [] Event too short-// ==============================--#if defined(__LIBREMIDI_DEBUG__)-std::ostream& operator<<(std::ostream& s, const libremidi::message& m)-{- s << "[ MIDI: ";- for (auto b : m)- s << (unsigned int)b << ' ';- s << "]\n";- return s;-}-#endif--namespace libremidi-{-namespace util-{-struct no_validator-{- static inline bool validate_track([[maybe_unused]] const midi_track& track) { return true; }-};--struct validator-{- static inline bool validate_track(const midi_track& track)- {- if (track.empty())- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "empty track" << std::endl;-#endif- return false;- }-- // Ensure that there is a unique EOT at the end of the track- auto it = std::find_if(track.begin(), track.end(), [](const libremidi::track_event& msg) {- static const auto eot = meta_events::end_of_track();- return msg.m.bytes == eot.bytes;- });-- if (it == track.end())- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "track has no END OF TRACK" << std::endl;-#endif- return false;- }-- if (&it->m != &track.back().m)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << std::distance(it, track.end());- std::cerr << "track does not end with END OF TRACK" << std::endl;-#endif- return false;- }-- return true;- }-};--// Used when we know that we have enough space-struct read_unchecked-{- // Read a MIDI-style variable-length integer (big-endian value in groups of 7 bits,- // with top bit set to signify that another byte follows).- static inline void ensure_size(- [[maybe_unused]] const uint8_t* begin, [[maybe_unused]] const uint8_t* end,- [[maybe_unused]] int64_t needed)- {- }-- static inline uint32_t- read_variable_length(uint8_t const*& data, [[maybe_unused]] uint8_t const* end)- {- uint32_t result = 0;- while (true)- {- uint8_t b = *data++;- if (b & 0x80)- {- result += (b & 0x7F);- result <<= 7;- }- else- {- return result + b; // b is the last byte- }- }- }-- static inline void read_bytes(- midi_bytes& buffer, uint8_t const*& data, [[maybe_unused]] const uint8_t* end,- const std::size_t num)- {- buffer.reserve(buffer.size() + num);- for (std::size_t i = 0; i < num; ++i)- buffer.push_back(*data++);- }-- static inline void read_bytes(- midi_bytes& buffer, uint8_t const*& data, [[maybe_unused]] const uint8_t* end, const int num)- {- read_bytes(buffer, data, end, static_cast<std::size_t>(num));- }-- static inline uint16_t read_uint16_be(uint8_t const*& data, [[maybe_unused]] const uint8_t* end)- {- uint16_t result = *data++ << 8u;- result += *data++;- return result;- }-- static inline uint32_t read_uint24_be(uint8_t const*& data, [[maybe_unused]] const uint8_t* end)- {- uint32_t result = *data++ << 16u;- result += static_cast<uint32_t>(*data++ << 8u);- result += *data++;- return result;- }-- static inline uint32_t read_uint32_be(uint8_t const*& data, [[maybe_unused]] const uint8_t* end)- {- uint32_t result = *data++ << 24u;- result += static_cast<uint32_t>(*data++ << 16u);- result += static_cast<uint32_t>(*data++ << 8u);- result += *data++;- return result;- }-};--// Used when we do not know if we have enough bytes and have to check before reading-struct read_checked-{- // Read a MIDI-style variable-length integer (big-endian value in groups of 7 bits,- // with top bit set to signify that another byte follows).- static inline void- ensure_size(const uint8_t* begin, const uint8_t* end, const std::size_t needed)- {- if (const auto available = static_cast<std::size_t>(end - begin); available < needed)- throw std::runtime_error("MIDI reader: not enough data to process");- }-- static inline std::size_t read_variable_length(uint8_t const*& data, uint8_t const* end)- {- std::size_t result = 0;- while (true)- {- ensure_size(data, end, 1);- uint8_t b = *data++;- if (b & 0x80)- {- result += (b & 0x7F);- result <<= 7;- }- else- {- return result + b; // b is the last byte- }- }- }-- static inline void- read_bytes(midi_bytes& buffer, uint8_t const*& data, uint8_t const* end, const std::size_t num)- {- ensure_size(data, end, num);- read_unchecked::read_bytes(buffer, data, end, num);- }-- static inline uint16_t read_uint16_be(uint8_t const*& data, uint8_t const* end)- {- ensure_size(data, end, 2);- return read_unchecked::read_uint16_be(data, end);- }-- static inline uint32_t read_uint24_be(uint8_t const*& data, uint8_t const* end)- {- ensure_size(data, end, 3);- return read_unchecked::read_uint24_be(data, end);- }-- static inline uint32_t read_uint32_be(uint8_t const*& data, uint8_t const* end)- {- ensure_size(data, end, 4);- return read_unchecked::read_uint32_be(data, end);- }-};-}--#if defined(LIBREMIDI_UNCHECKED)-using byte_reader = util::read_unchecked;-#else-using byte_reader = util::read_checked;-#endif--#if defined(LIBREMIDI_UNVALIDATED)-using validator = util::no_validator;-#else-using validator = util::validator;-#endif--LIBREMIDI_INLINE-track_event parse_event(- int tick, int track, const uint8_t*& dataStart, const uint8_t* dataEnd,- message_type lastEventTypeByte)-{- byte_reader::ensure_size(dataStart, dataEnd, 1);- auto type = static_cast<message_type>(*dataStart++);-- track_event event{tick, track, message{}};-- if ((static_cast<uint8_t>(type) & 0xF0) == 0xF0)- {- // Meta event- if (static_cast<uint8_t>(type) == 0xFF)- {- byte_reader::ensure_size(dataStart, dataEnd, 1);- auto subtype = static_cast<meta_event_type>(*dataStart++);-- event.m.bytes.reserve(3);- event.m.bytes.push_back(static_cast<uint8_t>(type));- event.m.bytes.push_back(static_cast<uint8_t>(subtype));-- uint32_t length = 0;- // Here we read the meta-event length manually, as this way we can also put it into- // event.m.bytes- while (true)- {- byte_reader::ensure_size(dataStart, dataEnd, 1);- uint8_t b = *dataStart++;- event.m.bytes.push_back(b);- if (b & 0x80)- {- const uint8_t byte = (b & 0x7F);-- length += byte;- length <<= 7;- }- else- {- length += b; // b is the last byte- break;- }- }-- switch (subtype)- {- case meta_event_type::SEQUENCE_NUMBER: {- switch (length)- {- case 0:- return event;- case 2:- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, 2);- return event;- default:- throw std::invalid_argument("Expected length for SEQUENCE_NUMBER event is 0 or 2");- }- }- case meta_event_type::TEXT:- case meta_event_type::COPYRIGHT:- case meta_event_type::TRACK_NAME:- case meta_event_type::INSTRUMENT:- case meta_event_type::LYRIC:- case meta_event_type::MARKER:- case meta_event_type::CUE:- case meta_event_type::PATCH_NAME:- case meta_event_type::DEVICE_NAME: {- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }-- case meta_event_type::END_OF_TRACK: {- if (length != 0)- throw std::invalid_argument("Expected length for END_OF_TRACK event is 0");- return event;- }- case meta_event_type::TEMPO_CHANGE: {- if (length != 3)- throw std::invalid_argument("Expected length for TEMPO_CHANGE event is 3");- // event.m.bytes[3] = read_uint24_be(dataStart); // @dimitri TOFIX- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }- case meta_event_type::SMPTE_OFFSET: {- if (length != 5)- throw std::invalid_argument("Expected length for SMPTE_OFFSET event is 5");- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- auto& b = event.m.bytes;-- uint8_t format = (b[3] & 0b01100000) >> 5;- uint8_t h = (b[3] & 0b00011111);-- if (format > 3)- throw std::invalid_argument("SMPTE_OFFSET has unknown format");-- int max = 0;- switch (format)- {- case 0: // 24- max = 24;- break;- case 1: // 25- max = 25;- break;- case 2: // 29- max = 29;- break;- case 3: // 30- max = 30;- break;- default:- break;- }-- if (h >= 24 || b[4] >= 60 || b[5] >= 60 || b[6] >= max || b[7] >= 100)- throw std::invalid_argument("SMPTE_OFFSET is out-of-23:59:59:xx:99 bounds");- return event;- }- case meta_event_type::TIME_SIGNATURE: {- if (length != 4)- throw std::invalid_argument("Expected length for TIME_SIGNATURE event is 4");- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }- case meta_event_type::KEY_SIGNATURE: {- if (length != 2)- throw std::invalid_argument("Expected length for KEY_SIGNATURE event is 2");- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- auto k = static_cast<int8_t>(event.m[3]);- if (k < -7 || k > 7)- throw std::invalid_argument("Invalid KEY_SIGNATURE");- if (event.m[4] > 1)- throw std::invalid_argument("Invalid KEY_SIGNATURE");- return event;- }- case meta_event_type::PROPRIETARY: {- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }- case meta_event_type::CHANNEL_PREFIX: {- if (length != 1)- throw std::invalid_argument("Expected length for CHANNEL_PREFIX event is 1");- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }- case meta_event_type::MIDI_PORT: {- if (length != 1)- throw std::invalid_argument("Expected length for MIDI_PORT event is 1");- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }- case meta_event_type::UNKNOWN:- default: {- // Unknown events?- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }- }- }-- else if (type == message_type::SYSTEM_EXCLUSIVE)- {- const auto length = byte_reader::read_variable_length(dataStart, dataEnd);- event.m.bytes = {static_cast<uint8_t>(type)};- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }-- else if (type == message_type::EOX)- {- const auto length = byte_reader::read_variable_length(dataStart, dataEnd);- byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);- return event;- }- else- {- throw std::runtime_error("Unrecognised MIDI event type byte");- }- }-- // Channel events- else- {- event.m.bytes.clear();-- // Running status...- if ((static_cast<uint8_t>(type) & 0x80) == 0)- {- // Reuse lastEventTypeByte as the event type.- // eventTypeByte is actually the first parameter- event.m.bytes.push_back(static_cast<uint8_t>(lastEventTypeByte));- event.m.bytes.push_back(static_cast<uint8_t>(type));- type = lastEventTypeByte;- }- else- {- event.m.bytes.push_back(static_cast<uint8_t>(type));-- byte_reader::ensure_size(dataStart, dataEnd, 1);- event.m.bytes.push_back(static_cast<uint8_t>(*dataStart++));- lastEventTypeByte = type;- }-- static constexpr auto validate = [](midi_bytes& b) {- if (b[1] < 128 && b[2] < 128)- return true;- throw std::invalid_argument("MIDI message has arguments > 127");- };-- switch (static_cast<message_type>(static_cast<uint8_t>(type) & 0xF0))- {- case message_type::NOTE_OFF:- case message_type::NOTE_ON:- case message_type::POLY_PRESSURE:- case message_type::CONTROL_CHANGE:- byte_reader::ensure_size(dataStart, dataEnd, 1);- event.m.bytes.push_back(*dataStart++);- validate(event.m.bytes);- return event;- case message_type::PROGRAM_CHANGE:- if (event.m.bytes[1] >= 128)- throw std::invalid_argument("MIDI PC has arguments > 127");- return event;- case message_type::AFTERTOUCH:- if (event.m.bytes[1] >= 128)- throw std::invalid_argument("MIDI Atertouch has arguments > 127");- return event;- case message_type::PITCH_BEND:- byte_reader::ensure_size(dataStart, dataEnd, 1);- event.m.bytes.push_back(*dataStart++);- validate(event.m.bytes);- return event;-- case message_type::TIME_CODE:- throw std::runtime_error("Unsupported MIDI event type TIME_CODE");- case message_type::SONG_POS_POINTER:- throw std::runtime_error("Unsupported MIDI event type SONG_POS_POINTER");- case message_type::SONG_SELECT:- throw std::runtime_error("Unsupported MIDI event type SONG_SELECT");- case message_type::RESERVED1:- throw std::runtime_error("Unsupported MIDI event type RESERVED1");- case message_type::RESERVED2:- throw std::runtime_error("Unsupported MIDI event type RESERVED2");- case message_type::TUNE_REQUEST:- throw std::runtime_error("Unsupported MIDI event type TUNE_REQUEST");- case message_type::EOX:- throw std::runtime_error("Unsupported MIDI event type EOX");- // System Realtime Messages :- case message_type::TIME_CLOCK:- throw std::runtime_error("Unsupported MIDI event type TIME_CLOCK");- case message_type::RESERVED3:- throw std::runtime_error("Unsupported MIDI event type RESERVED3");- case message_type::START:- throw std::runtime_error("Unsupported MIDI event type START");- case message_type::CONTINUE:- throw std::runtime_error("Unsupported MIDI event type CONTINUE");- case message_type::STOP:- throw std::runtime_error("Unsupported MIDI event type STOP");- case message_type::RESERVED4:- throw std::runtime_error("Unsupported MIDI event type RESERVED4");- case message_type::ACTIVE_SENSING:- throw std::runtime_error("Unsupported MIDI event type ACTIVE_SENSING");- case message_type::SYSTEM_RESET:- throw std::runtime_error("Unsupported MIDI event type SYSTEM_RESET");- case message_type::INVALID:- throw std::runtime_error("Unsupported MIDI event type INVALID");- case message_type::SYSTEM_EXCLUSIVE:- throw std::runtime_error("Unsupported MIDI event type SYSTEM_EXCLUSIVE");- default:- throw std::runtime_error("Unsupported MIDI event type");- }- }-}--LIBREMIDI_INLINE-reader::reader(bool useAbsolute)- : ticksPerBeat(480)- , startingTempo(120)- , useAbsoluteTicks(useAbsolute)-{-}--LIBREMIDI_INLINE-reader::~reader() { }--constexpr int str_to_headerid(const char* str)-{- return str[0] << 24 | str[1] << 16 | str[2] << 8 | str[3];-}--LIBREMIDI_INLINE-auto reader::parse(const uint8_t* dataPtr, std::size_t size) noexcept -> parse_result-try-{- using namespace libremidi::util;-- tracks.clear();-- if (size == 0)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "empty buffer passed to parse." << std::endl;-#endif- return parse_result::invalid;- }-- const uint8_t* const dataEnd = dataPtr + size;-- uint32_t headerId = read_checked::read_uint32_be(dataPtr, dataEnd);- uint32_t headerLength = read_checked::read_uint32_be(dataPtr, dataEnd);-- if (static_cast<int>(headerId) != str_to_headerid("MThd") || headerLength != 6)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "couldn't parse header" << std::endl;-#endif- return parse_result::invalid;- }-- format = read_checked::read_uint16_be(- dataPtr, dataEnd); //@tofix format type -> save for later eventually- if (format > 2)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "unknown format" << std::endl;-#endif- return parse_result::invalid;- }- int trackCount = read_checked::read_uint16_be(dataPtr, dataEnd);- uint16_t timeDivision = read_checked::read_uint16_be(dataPtr, dataEnd);-- // CBB: deal with the SMPTE style time coding- // timeDivision is described here http://www.sonicspot.com/guide/midifiles.html- if (timeDivision & 0x8000)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "found SMPTE time frames (unsupported)" << std::endl;- int fps = (timeDivision >> 16) & 0x7f;- if (fps != -30 && fps != -29 && fps != -25 && fps != -24)- return parse_result::invalid;- int ticksPerFrame = timeDivision & 0xff;-#endif- // given beats per second, timeDivision should be derivable.- return parse_result::invalid;- }-- startingTempo = 120.0f; // midi default- ticksPerBeat = float(timeDivision); // ticks per beat (a beat is defined as a quarter note)-- parse_result result = parse_result::validated;-- for (int i = 0; i < trackCount; ++i)- {- midi_track track;-- headerId = read_checked::read_uint32_be(dataPtr, dataEnd);- headerLength = read_checked::read_uint32_be(dataPtr, dataEnd);-- if (headerId != str_to_headerid("MTrk"))- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "couldn't find track header" << std::endl;-#endif- return parse_result::incomplete;- }-- int64_t available = dataEnd - dataPtr;- if (available < headerLength)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "not enough data available" << std::endl;-#endif- return parse_result::incomplete;- }-- track.reserve(headerLength / 3);-- const uint8_t* const trackEnd = dataPtr + headerLength;-- auto runningEvent = message_type::INVALID;-- std::size_t tickCount = 0;-- while (dataPtr < trackEnd)- {- const auto tick = read_checked::read_variable_length(dataPtr, trackEnd);- if (useAbsoluteTicks)- {- tickCount += tick;- }- else- {- tickCount = tick;- }-- try- {- track_event ev- = parse_event(static_cast<int>(tickCount), i, dataPtr, trackEnd, runningEvent);- if (!ev.m.empty())- {- if (!ev.m.is_meta_event())- {- runningEvent = static_cast<message_type>(ev.m.bytes[0]);- }- }- else- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "could not read event" << std::endl;-#endif- dataPtr = trackEnd;- result = parse_result::incomplete;- continue;- }-- track.push_back(std::move(ev));- }- catch (const std::exception& e)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "" << e.what() << std::endl;-#endif- dataPtr = trackEnd;- result = parse_result::incomplete;- continue;- }- }-- if (result == parse_result::validated)- {- if (!validator::validate_track(track))- {- result = parse_result::complete;- }- }- tracks.push_back(std::move(track));- }-- if (result == parse_result::validated)- {- if (dataPtr != dataEnd)- {-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "midifile has junk at end: " << std::intptr_t(dataEnd - dataPtr) << std::endl;-#endif- result = parse_result::complete;- }- }- return result;-}-catch (const std::exception& e)-{-#if defined(__LIBREMIDI_DEBUG__)- std::cerr << "" << e.what() << std::endl;-#endif- return parse_result::invalid;-}--// In ticks-LIBREMIDI_INLINE-double reader::get_end_time() const noexcept-{- if (useAbsoluteTicks)- {- double totalLength = 0.;- for (const auto& t : tracks)- {- if (!t.empty())- {- const auto& last_event = t.back();- if (last_event.tick > totalLength)- totalLength = last_event.tick;- }- }- return totalLength;- }- else- {- double totalLength = 0.;- for (const auto& t : tracks)- {- double trackLength = 0.;- for (const auto& e : t)- trackLength += e.tick;-- if (trackLength > totalLength)- totalLength = trackLength;- }- return totalLength;- }-}--LIBREMIDI_INLINE-auto reader::parse(const std::vector<uint8_t>& buffer) noexcept -> parse_result-{- return parse(buffer.data(), buffer.size());-}--#if defined(LIBREMIDI_HAS_SPAN)-LIBREMIDI_INLINE-auto reader::parse(std::span<uint8_t> buffer) noexcept -> parse_result-{- return parse(buffer.data(), buffer.size());-}-#endif-}
− libremidi/writer.cpp
@@ -1,213 +0,0 @@-/*-Copyright (c) 2015, Dimitri Diakopoulos All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:--* Redistributions of source code must retain the above copyright notice, this- list of conditions and the following disclaimer.--* Redistributions in binary form must reproduce the above copyright notice,- this list of conditions and the following disclaimer in the documentation- and/or other materials provided with the distribution.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.-*/--#if !defined(LIBREMIDI_HEADER_ONLY)- #include <libremidi/writer.hpp>-#endif-#include <algorithm>-#include <bit>-#include <ostream>-#include <string>--#if !defined(__cpp_lib_bit_cast)-namespace std-{-template <typename R, typename T>-[[nodiscard]]-constexpr R bit_cast(const T& v) noexcept-{- union- {- R res;- T init;- } u{.init = v};- return u.res;-}-}-#endif-namespace libremidi-{-namespace util-{-template <typename T>-static LIBREMIDI_INLINE std::ostream& write_be(std::ostream& out, T value)-{- static_assert(- std::endian::native == std::endian::big || std::endian::native == std::endian::little);- if constexpr (std::endian::native == std::endian::big)- {- out << value;- }- else- {- static constexpr auto N = sizeof(value);- struct storage- {- uint8_t bytes[N];- };- auto data = std::bit_cast<storage>(value);- std::reverse(data.bytes, data.bytes + N);- out.write(reinterpret_cast<const char*>(data.bytes), static_cast<std::streamsize>(N));- }- return out;-}--// Write a number to the midifile-// as a variable length value which segments a file into 7-bit-// values. Maximum size of aValue is 0x7fffffff-static LIBREMIDI_INLINE void write_variable_length(uint32_t aValue, std::vector<uint8_t>& outdata)-{- uint8_t bytes[5] = {0};-- bytes[0] = static_cast<uint8_t>((aValue >> 28) & 0x7F); // most significant 5 bits- bytes[1] = static_cast<uint8_t>((aValue >> 21) & 0x7F); // next largest 7 bits- bytes[2] = static_cast<uint8_t>((aValue >> 14) & 0x7F);- bytes[3] = static_cast<uint8_t>((aValue >> 7) & 0x7F);- bytes[4] = static_cast<uint8_t>((aValue) & 0x7F); // least significant 7 bits-- int start = 0;- while (start < 5 && bytes[start] == 0)- start++;-- for (int i = start; i < 4; i++)- {- bytes[i] = bytes[i] | 0x80;- outdata.push_back(bytes[i]);- }- outdata.push_back(bytes[4]);-}--static LIBREMIDI_INLINE void-add_event_track_count_check(std::vector<midi_track>& tracks, int track)-{- if (track < 0)- throw std::out_of_range("Refusing to add an event to track " + std::to_string(track) + ".");- if (track > 65535)- throw std::out_of_range(- "Refusing to add an event to track " + std::to_string(track)- + " ; change add_event_track_count_check in libremidi writer.cpp to increase the limit.");-- while (tracks.size() < static_cast<std::size_t>(track + 1)) // NOLINT(*-misplaced-widening-cast)- tracks.emplace_back();-}-}--LIBREMIDI_INLINE-void writer::add_event(const int tick, const int track, const message& m)-{- util::add_event_track_count_check(tracks, track);-- tracks[static_cast<std::size_t>(track)].push_back({tick, track, m});-}--LIBREMIDI_INLINE-void writer::add_event(int track, const track_event& m)-{- util::add_event_track_count_check(tracks, track);-- tracks[static_cast<std::size_t>(track)].push_back(m);-}--LIBREMIDI_INLINE-void writer::add_track()-{- util::add_event_track_count_check(tracks, static_cast<int>(tracks.size() + 1));-}--LIBREMIDI_INLINE-void writer::write(std::ostream& out) const-{- // MIDI File Header- out.write("MThd", 4);- util::write_be<uint32_t>(out, 6);- util::write_be<uint16_t>(out, (tracks.size() == 1) ? 0 : 1);- util::write_be<uint16_t>(out, static_cast<uint16_t>(tracks.size()));- util::write_be<uint16_t>(out, ticksPerQuarterNote);-- std::vector<uint8_t> trackRawData;- for (const auto& event_list : tracks)- {- trackRawData.clear();- // Rough estimation of the memory to allocate- trackRawData.reserve(event_list.size() * 3);-- for (const auto& event : event_list)- {- const auto& msg = event.m;- if (msg.empty())- continue;-- // Suppress end-of-track meta messages (one will be added- // automatically after all track data has been written).- if (msg.get_meta_event_type() == meta_event_type::END_OF_TRACK)- continue;-- util::write_variable_length(static_cast<uint32_t>(event.tick), trackRawData);-- if ((msg.get_message_type() == message_type::SYSTEM_EXCLUSIVE)- || (event.m.get_message_type() == message_type::EOX))- {- // 0xf0 == Complete sysex message (0xf0 is part of the raw MIDI).- // 0xf7 == Raw byte message (0xf7 not part of the raw MIDI).- // Print the first byte of the message (0xf0 or 0xf7), then- // print a VLV length for the rest of the bytes in the message.- // In other words, when creating a 0xf0 or 0xf7 MIDI message,- // do not insert the VLV byte length yourself, as this code will- // do it for you automatically.- trackRawData.emplace_back(msg.bytes[0]); // 0xf0 or 0xf7;-- util::write_variable_length(static_cast<uint32_t>(msg.size()) - 1, trackRawData);-- trackRawData.insert(- trackRawData.end(), msg.bytes.data() + 1, msg.bytes.data() + msg.bytes.size());- }- else- {- // Non-sysex type of message, so just output the bytes of the message:- trackRawData.insert(- trackRawData.end(), msg.bytes.data(), msg.bytes.data() + msg.bytes.size());- }- }-- auto size = trackRawData.size();- const auto eot = meta_events::end_of_track();-- if ((size < 3) || !((trackRawData[size - 3] == 0xFF) && (trackRawData[size - 2] == 0x2F)))- {- trackRawData.emplace_back(0x0); // tick- trackRawData.emplace_back(eot[0]);- trackRawData.emplace_back(eot[1]);- trackRawData.emplace_back(eot[2]);- }-- // Write the track ID marker "MTrk":- out.write("MTrk", 4);- util::write_be<uint32_t>(out, static_cast<uint32_t>(trackRawData.size()));- out.write(- reinterpret_cast<char*>(trackRawData.data()),- static_cast<std::streamsize>(trackRawData.size()));- }-}-}
+ vendor/libremidi/api-c.h view
@@ -0,0 +1,39 @@+#pragma once++#if __cplusplus+extern "C" {+#endif++//! MIDI API specifier arguments.+//! To get information on which feature is supported by each back-end, check their backend file+//! in e.g. backends/winmm.hpp, etc.+typedef enum libremidi_api+{+ UNSPECIFIED = 0x0, /*!< Search for a working compiled API. */++ // MIDI 1.0 APIs+ COREMIDI = 0x1, /*!< macOS CoreMidi API. */+ ALSA_SEQ, /*!< Linux ALSA Sequencer API. */+ ALSA_RAW, /*!< Linux Raw ALSA API. */+ JACK_MIDI, /*!< JACK Low-Latency MIDI Server API. */+ WINDOWS_MM, /*!< Microsoft Multimedia MIDI API. */+ WINDOWS_UWP, /*!< Microsoft WinRT MIDI API. */+ WEBMIDI, /*!< Web MIDI API through Emscripten */+ PIPEWIRE, /*!< PipeWire */+ KEYBOARD, /*!< Computer keyboard input */+ NETWORK, /*!< MIDI over IP */++ // MIDI 2.0 APIs+ ALSA_RAW_UMP = 0x1000, /*!< Raw ALSA API for MIDI 2.0 */+ ALSA_SEQ_UMP, /*!< Linux ALSA Sequencer API for MIDI 2.0 */+ COREMIDI_UMP, /*!< macOS CoreMidi API for MIDI 2.0. Requires macOS 11+ */+ WINDOWS_MIDI_SERVICES, /*!< Windows API for MIDI 2.0. Requires Windows 11 */+ KEYBOARD_UMP, /*!< Computer keyboard input */+ NETWORK_UMP, /*!< MIDI2 over IP */++ DUMMY = 0xFFFF /*!< A compilable but non-functional API. */+} libremidi_api;++#if __cplusplus+}+#endif
+ vendor/libremidi/client.cpp view
@@ -0,0 +1,47 @@+#if !defined(LIBREMIDI_HEADER_ONLY)+ #include "client.hpp"+#endif+#include <libremidi/backends.hpp>+#include <libremidi/shared_context.hpp>++#ifdef LIBREMIDI_ALSA+ #include <libremidi/backends/alsa_seq/shared_handler.hpp>+#endif+#ifdef LIBREMIDI_JACK+ #include <libremidi/backends/jack/shared_handler.hpp>+#endif+namespace libremidi+{+LIBREMIDI_INLINE+shared_configurations+create_shared_context(const libremidi::API api, [[maybe_unused]] std::string_view client_name)+{+ switch (api)+ {+#if __has_include(<boost/lockfree/spsc_queue.hpp>)+ #if defined(LIBREMIDI_ALSA)+ case libremidi::API::ALSA_SEQ:+ return alsa_seq::shared_handler::make(client_name);+ #endif++ #if defined(LIBREMIDI_JACK)+ case libremidi::API::JACK_MIDI:+ return jack::shared_handler::make(client_name);+ #endif+#endif+ case libremidi::API::COREMIDI:+ case libremidi::API::COREMIDI_UMP:+ // TODO++ case libremidi::API::WINDOWS_MIDI_SERVICES:+ // TODO++ default:+ return {+ .context = {},+ .observer = observer_configuration_for(api),+ .in = midi_in_configuration_for(api),+ .out = midi_out_configuration_for(api)};+ }+}+}
+ vendor/libremidi/libremidi-c.cpp view
@@ -0,0 +1,507 @@+// 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;+}+}
+ vendor/libremidi/libremidi-c.h view
@@ -0,0 +1,275 @@+#pragma once+#include <libremidi/api-c.h>++#include <stdbool.h>+#include <stddef.h>+#include <stdint.h>++#if defined(LIBREMIDI_EXPORTS)+ #if defined(_MSC_VER)+ #define LIBREMIDI_EXPORT __declspec(dllexport)+ #elif defined(__GNUC__) || defined(__clang__)+ #define LIBREMIDI_EXPORT __attribute__((visibility("default")))+ #endif+#else+ #define LIBREMIDI_EXPORT+#endif++#if __cplusplus+extern "C" {+#endif+typedef unsigned char libremidi_midi1_symbol;+typedef libremidi_midi1_symbol* libremidi_midi1_message;++typedef uint32_t libremidi_midi2_symbol;+typedef libremidi_midi2_symbol* libremidi_midi2_message;++typedef int64_t libremidi_timestamp;++typedef struct libremidi_midi_in_port libremidi_midi_in_port;+typedef struct libremidi_midi_out_port libremidi_midi_out_port;+typedef struct libremidi_midi_in_handle libremidi_midi_in_handle;+typedef struct libremidi_midi_out_handle libremidi_midi_out_handle;+typedef struct libremidi_midi_observer_handle libremidi_midi_observer_handle;++typedef struct libremidi_api_configuration libremidi_api_configuration;++enum libremidi_timestamp_mode+{+ NoTimestamp,++ Relative,+ Absolute,+ SystemMonotonic,+ AudioFrame,+ Custom+};++typedef struct libremidi_api_configuration+{+ enum libremidi_api api;+ enum+ {+ Observer,+ Input,+ Output+ } configuration_type;++ void* data;+} libremidi_api_configuration;++typedef struct libremidi_observer_configuration+{+ struct+ {+ void* context;+ void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);+ } on_error;+ struct+ {+ void* context;+ void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);+ } on_warning;++ struct+ {+ void* context;+ void (*callback)(void* ctx, const libremidi_midi_in_port*);+ } input_added;+ struct+ {+ void* context;+ void (*callback)(void* ctx, const libremidi_midi_in_port*);+ } input_removed;+ struct+ {+ void* context;+ void (*callback)(void* ctx, const libremidi_midi_out_port*);+ } output_added;+ struct+ {+ void* context;+ void (*callback)(void* ctx, const libremidi_midi_out_port*);+ } output_removed;++ bool track_hardware;+ bool track_virtual;+ bool track_any;+ bool notify_in_constructor;+} libremidi_observer_configuration;++typedef struct libremidi_midi1_callback+{+ void* context;+ void (*callback)(void* ctx, libremidi_timestamp, const libremidi_midi1_symbol*, size_t len);+} libremidi_midi1_callback;++typedef struct libremidi_midi2_callback+{+ void* context;+ void (*callback)(void* ctx, libremidi_timestamp, const libremidi_midi2_symbol*, size_t len);+} libremidi_midi2_callback;++typedef struct libremidi_midi_configuration+{+ // Indicates the kind of callback requested, e.g. set MIDI1_RAW if you use the on_midi1_raw_data+ enum+ {+ MIDI1 = (1 << 1),+ MIDI1_RAW = (1 << 2),+ MIDI2 = (1 << 3),+ MIDI2_RAW = (1 << 4)+ } version;++ union+ {+ libremidi_midi_in_port* in_port;+ libremidi_midi_out_port* out_port;+ };++ union+ {+ libremidi_midi1_callback on_midi1_message;+ libremidi_midi1_callback on_midi1_raw_data;+ libremidi_midi2_callback on_midi2_message;+ libremidi_midi2_callback on_midi2_raw_data;+ };++ struct+ {+ void* context;+ libremidi_timestamp (*callback)(void* ctx, libremidi_timestamp);+ } get_timestamp;++ struct+ {+ void* context;+ void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);+ } on_error;+ struct+ {+ void* context;+ void (*callback)(void* ctx, const char* error, size_t error_len, const void* source_location);+ } on_warning;++ const char* port_name;+ bool virtual_port;++ bool ignore_sysex;+ bool ignore_timing;+ bool ignore_sensing;++ enum libremidi_timestamp_mode timestamps;+} libremidi_midi_configuration;++/// API utilities+LIBREMIDI_EXPORT+const char* libremidi_get_version(void);++LIBREMIDI_EXPORT+void libremidi_midi1_available_apis(void* ctx, void (*)(void*, libremidi_api));+LIBREMIDI_EXPORT+void libremidi_midi2_available_apis(void* ctx, void (*)(void*, libremidi_api));+LIBREMIDI_EXPORT+const char* libremidi_api_identifier(libremidi_api);+LIBREMIDI_EXPORT+const char* libremidi_api_display_name(libremidi_api);+LIBREMIDI_EXPORT+libremidi_api libremidi_get_compiled_api_by_identifier(const char*);++/// Create configurations+LIBREMIDI_EXPORT+int libremidi_midi_api_configuration_init(libremidi_api_configuration*);++LIBREMIDI_EXPORT+int libremidi_midi_observer_configuration_init(libremidi_observer_configuration*);++LIBREMIDI_EXPORT+int libremidi_midi_configuration_init(libremidi_midi_configuration*);++/// Read information about port objects+LIBREMIDI_EXPORT+int libremidi_midi_in_port_clone(const libremidi_midi_in_port* port, libremidi_midi_in_port** dst);++LIBREMIDI_EXPORT+int libremidi_midi_in_port_free(libremidi_midi_in_port* port);++LIBREMIDI_EXPORT+int libremidi_midi_in_port_name(+ const libremidi_midi_in_port* port, const char** name, size_t* len);++LIBREMIDI_EXPORT+int libremidi_midi_out_port_clone(+ const libremidi_midi_out_port* port, libremidi_midi_out_port** dst);++LIBREMIDI_EXPORT+int libremidi_midi_out_port_free(libremidi_midi_out_port* port);++LIBREMIDI_EXPORT+int libremidi_midi_out_port_name(+ const libremidi_midi_out_port* port, const char** name, size_t* len);++/// Observer API+LIBREMIDI_EXPORT+int libremidi_midi_observer_new(+ const libremidi_observer_configuration*, libremidi_api_configuration*,+ libremidi_midi_observer_handle**);++LIBREMIDI_EXPORT+int libremidi_midi_observer_enumerate_input_ports(+ libremidi_midi_observer_handle*, void* context,+ void (*)(void* ctx, const libremidi_midi_in_port*));++LIBREMIDI_EXPORT+int libremidi_midi_observer_enumerate_output_ports(+ libremidi_midi_observer_handle*, void* context,+ void (*)(void* ctx, const libremidi_midi_out_port*));++LIBREMIDI_EXPORT+int libremidi_midi_observer_free(libremidi_midi_observer_handle*);++/// MIDI input API (read MIDI messages)+LIBREMIDI_EXPORT+int libremidi_midi_in_new(+ const libremidi_midi_configuration*, const libremidi_api_configuration*,+ libremidi_midi_in_handle**);++LIBREMIDI_EXPORT+int libremidi_midi_in_is_connected(const libremidi_midi_in_handle*);++LIBREMIDI_EXPORT+libremidi_timestamp libremidi_midi_in_absolute_timestamp(libremidi_midi_in_handle*);++LIBREMIDI_EXPORT+int libremidi_midi_in_free(libremidi_midi_in_handle*);++/// MIDI output API (send MIDI messages)+LIBREMIDI_EXPORT+int libremidi_midi_out_new(+ const libremidi_midi_configuration*, const libremidi_api_configuration*,+ libremidi_midi_out_handle**);++LIBREMIDI_EXPORT+int libremidi_midi_out_is_connected(const libremidi_midi_out_handle*);++LIBREMIDI_EXPORT+int libremidi_midi_out_send_message(+ libremidi_midi_out_handle*, const libremidi_midi1_symbol*, size_t);++LIBREMIDI_EXPORT+int libremidi_midi_out_send_ump(libremidi_midi_out_handle*, const libremidi_midi2_symbol*, size_t);++LIBREMIDI_EXPORT+int libremidi_midi_out_schedule_message(+ libremidi_midi_out_handle*, int64_t ts, const libremidi_midi1_symbol*, size_t);++LIBREMIDI_EXPORT+int libremidi_midi_out_schedule_ump(+ libremidi_midi_out_handle*, int64_t ts, const libremidi_midi2_symbol*, size_t);++LIBREMIDI_EXPORT+int libremidi_midi_out_free(libremidi_midi_out_handle*);++#if __cplusplus+}+#endif
+ vendor/libremidi/libremidi.cpp view
@@ -0,0 +1,186 @@+#if !defined(LIBREMIDI_HEADER_ONLY)+ #include <libremidi/libremidi.hpp>+#endif++#include <libremidi/backends.hpp>+#include <libremidi/detail/midi_api.hpp>++#include <cmath>++namespace libremidi+{++LIBREMIDI_INLINE+std::string_view get_version() noexcept+{+ return LIBREMIDI_VERSION;+}++LIBREMIDI_INLINE std::string_view get_api_name(libremidi::API api)+{+ std::string_view ret;+ midi_any::for_backend(api, [&](auto& b) { ret = b.name; });+ return ret;+}++LIBREMIDI_INLINE std::string_view get_api_display_name(libremidi::API api)+{+ std::string_view ret;+ midi_any::for_backend(api, [&](auto& b) { ret = b.display_name; });+ return ret;+}++LIBREMIDI_INLINE libremidi::API get_compiled_api_by_name(std::string_view name)+{+ libremidi::API ret = libremidi::API::UNSPECIFIED;+ midi_any::for_all_backends([&](auto& b) {+ if (name == b.name)+ ret = b.API;+ });+ return ret;+}++[[nodiscard]] LIBREMIDI_INLINE std::vector<libremidi::API> available_apis() noexcept+{+ std::vector<libremidi::API> apis;+ midi1::for_all_backends([&](auto b) { apis.push_back(b.API); });+ return apis;+}++[[nodiscard]] LIBREMIDI_INLINE std::vector<libremidi::API> available_ump_apis() noexcept+{+ std::vector<libremidi::API> apis;+ midi2::for_all_backends([&](auto b) { apis.push_back(b.API); });+ return apis;+}+LIBREMIDI_INLINE+libremidi::API midi_api(const std::any& conf)+{+ libremidi::API ret = libremidi::API::UNSPECIFIED;+ midi_any::for_all_backends([&]<typename T>(T) {+ if (std::any_cast<typename T::midi_in_configuration>(&conf)+ || std::any_cast<typename T::midi_out_configuration>(&conf)+ || std::any_cast<typename T::midi_observer_configuration>(&conf))+ {+ ret = T::API;+ }+ });+ return ret;+}+LIBREMIDI_INLINE+std::any midi_in_configuration_for(libremidi::API api)+{+ std::any ret;+ midi_any::for_backend(api, [&]<typename T>(T) {+ using conf_type = typename T::midi_in_configuration;+ ret = conf_type{};+ });+ return ret;+}++LIBREMIDI_INLINE+std::any midi_out_configuration_for(libremidi::API api)+{+ std::any ret;+ midi_any::for_backend(api, [&]<typename T>(T) {+ using conf_type = typename T::midi_out_configuration;+ ret = conf_type{};+ });+ return ret;+}++LIBREMIDI_INLINE+std::any observer_configuration_for(libremidi::API api)+{+ std::any ret;+ midi_any::for_backend(api, [&]<typename T>(T) {+ using conf_type = typename T::midi_observer_configuration;+ ret = conf_type{};+ });+ return ret;+}++LIBREMIDI_INLINE+std::any midi_in_configuration_for(const libremidi::observer& obs)+{+ return midi_in_configuration_for(obs.get_current_api());+}++LIBREMIDI_INLINE+std::any midi_out_configuration_for(const libremidi::observer& obs)+{+ // FIXME reuse context when meaningful, e.g. ALSA, JACK...+ return midi_out_configuration_for(obs.get_current_api());+}++LIBREMIDI_INLINE+std::optional<input_port> in_default_port(libremidi::API api) noexcept+try+{+ libremidi::observer obs{{}, observer_configuration_for(api)};+ if (auto ports = obs.get_input_ports(); !ports.empty())+ return ports.front();+ return std::nullopt;+}+catch (const std::exception& e)+{+ return std::nullopt;+}++LIBREMIDI_INLINE+std::optional<output_port> out_default_port(libremidi::API api) noexcept+try+{+ libremidi::observer obs{{}, observer_configuration_for(api)};+ if (auto ports = obs.get_output_ports(); !ports.empty())+ return ports.front();+ return std::nullopt;+}+catch (const std::exception& e)+{+ return std::nullopt;+}++namespace midi1+{+LIBREMIDI_INLINE+std::any in_default_configuration()+{+ return midi_in_configuration_for(default_api());+}++LIBREMIDI_INLINE+std::any out_default_configuration()+{+ return midi_out_configuration_for(default_api());+}++LIBREMIDI_INLINE+std::any observer_default_configuration()+{+ return observer_configuration_for(default_api());+}+}++namespace midi2+{+LIBREMIDI_INLINE+std::any in_default_configuration()+{+ return midi_in_configuration_for(default_api());+}++LIBREMIDI_INLINE+std::any out_default_configuration()+{+ return midi_out_configuration_for(default_api());+}++LIBREMIDI_INLINE+std::any observer_default_configuration()+{+ return observer_configuration_for(default_api());+}+}++}
+ vendor/libremidi/midi_in.cpp view
@@ -0,0 +1,373 @@+#if !defined(LIBREMIDI_HEADER_ONLY)+ #include <libremidi/libremidi.hpp>+#endif++#include <libremidi/backends.hpp>+#include <libremidi/detail/midi_api.hpp>++#include <cassert>++namespace libremidi+{++static libremidi::ump_input_configuration+convert_midi1_to_midi2_input_configuration(const input_configuration& base_conf) noexcept+{+ libremidi::ump_input_configuration c2;+ c2.on_message = [cb = base_conf.on_message,+ converter = midi2_to_midi1{}](libremidi::ump&& msg) mutable -> void {+ converter.convert(+ msg.data, 1, msg.timestamp, [cb](const unsigned char* midi, std::size_t n, int64_t ts) {+ cb(libremidi::message{{midi, midi + n}, ts});+ return stdx::error{};+ });+ };+ c2.get_timestamp = base_conf.get_timestamp;+ c2.on_error = base_conf.on_error;+ c2.on_warning = base_conf.on_warning;+ c2.ignore_sysex = base_conf.ignore_sysex;+ c2.ignore_timing = base_conf.ignore_timing;+ c2.ignore_sensing = base_conf.ignore_sensing;+ c2.timestamps = base_conf.timestamps;+ return c2;+}++static libremidi::input_configuration+convert_midi2_to_midi1_input_configuration(const ump_input_configuration& base_conf) noexcept+{+ libremidi::input_configuration c2;+ c2.on_message = [cb = base_conf.on_message,+ converter = midi1_to_midi2{}](libremidi::message&& msg) mutable -> void {+ converter.convert(+ msg.bytes.data(), msg.bytes.size(), msg.timestamp,+ [cb](const uint32_t* ump, std::size_t n, int64_t ts) {+ if(n >= 4)+ {+ libremidi::ump u{ump[0],ump[1],ump[2],ump[3]};+ u.timestamp = ts;+ cb(std::move(u));+ }+ return stdx::error{};+ });+ };+ c2.get_timestamp = base_conf.get_timestamp;+ c2.on_error = base_conf.on_error;+ c2.on_warning = base_conf.on_warning;+ c2.ignore_sysex = base_conf.ignore_sysex;+ c2.ignore_timing = base_conf.ignore_timing;+ c2.ignore_sensing = base_conf.ignore_sensing;+ c2.timestamps = base_conf.timestamps;+ return c2;+}++static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>+make_midi_in(auto base_conf, std::any api_conf, auto backends)+{+ std::unique_ptr<midi_in_api> ptr;++ assert(base_conf.on_message || base_conf.on_raw_data);++ auto from_api = [&]<typename T>(T& /*backend*/) mutable {+ if (auto conf = std::any_cast<typename T::midi_in_configuration>(&api_conf))+ {+ ptr = libremidi::make<typename T::midi_in>(std::move(base_conf), std::move(*conf));+ return true;+ }+ return false;+ };+ std::apply([&](auto&&... b) { (from_api(b) || ...); }, backends);+ return ptr;+}++/// MIDI 1 helpers+static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>+make_midi1_in(const input_configuration& base_conf)+{+ for (const auto& api : available_apis())+ {+ try+ {+ auto impl+ = make_midi_in(base_conf, midi_in_configuration_for(api), midi1::available_backends);+ if (impl)+ return impl;+ }+ catch (const std::exception& e)+ {+ }+ }++ // No MIDI 1 backend, try the MIDI 2 ones with a wrap:+ {+ auto c2 = convert_midi1_to_midi2_input_configuration(base_conf);+ for (const auto& api : available_ump_apis())+ {+ try+ {+ auto impl = make_midi_in(c2, midi_in_configuration_for(api), midi2::available_backends);+ if (impl)+ return impl;+ }+ catch (const std::exception& e)+ {+ }+ }+ }++ return std::make_unique<midi_in_dummy>(input_configuration{}, dummy_configuration{});+}++static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>+make_midi1_in(const input_configuration& base_conf, const std::any& api_conf, libremidi::API api)+{+ if (libremidi::is_midi1(api))+ {+ return make_midi_in(base_conf, api_conf, midi1::available_backends);+ }+ else if (libremidi::is_midi2(api))+ {+ auto c2 = convert_midi1_to_midi2_input_configuration(base_conf);+ return make_midi_in(c2, api_conf, midi2::available_backends);+ }+ return {};+}++static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>+make_midi1_in(const input_configuration& base_conf, const std::any& api_conf)+{+ if (!api_conf.has_value())+ {+ return make_midi1_in(base_conf);+ }+ else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))+ {+ if (*api_p == libremidi::API::UNSPECIFIED)+ {+ return make_midi1_in(base_conf);+ }+ else+ {+ return make_midi1_in(base_conf, midi_in_configuration_for(*api_p), *api_p);+ }+ }+ else+ {+ if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)+ return {};+ else+ return make_midi1_in(base_conf, api_conf, libremidi::midi_api(api_conf));+ }+}++/// MIDI 1 constructors+LIBREMIDI_INLINE midi_in::midi_in(const input_configuration& base_conf) noexcept+ : impl_{make_midi1_in(base_conf)}+{+}++LIBREMIDI_INLINE+midi_in::midi_in(input_configuration base_conf, std::any api_conf)+ : impl_{make_midi1_in(base_conf, api_conf)}+{+ if (!impl_)+ {+ error_handler e;+ e.libremidi_handle_error(base_conf, "Could not open midi in for the given api");+ impl_ = std::make_unique<midi_in_dummy>(input_configuration{}, dummy_configuration{});+ }+}++/// MIDI 2 helpers+static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>+make_midi2_in(const ump_input_configuration& base_conf)+{+ for (const auto& api : available_ump_apis())+ {+ try+ {+ if (auto ret+ = make_midi_in(base_conf, midi_in_configuration_for(api), midi2::available_backends))+ return ret;+ }+ catch (const std::exception& e)+ {+ }+ }++ // No MIDI 2 backend, try the MIDI 1 ones with a wrap:+ {+ auto c2 = convert_midi2_to_midi1_input_configuration(base_conf);+ for (const auto& api : available_apis())+ {+ try+ {+ if (auto ret = make_midi_in(c2, midi_in_configuration_for(api), midi1::available_backends))+ return ret;+ }+ catch (const std::exception& e)+ {+ }+ }+ }++ return {};+}++static LIBREMIDI_INLINE std::unique_ptr<midi_in_api> make_midi2_in(+ const ump_input_configuration& base_conf, const std::any& api_conf, libremidi::API api)+{+ if (is_midi2(api))+ {+ return make_midi_in(base_conf, api_conf, midi2::available_backends);+ }+ else if (is_midi1(api))+ {+ auto c2 = convert_midi2_to_midi1_input_configuration(base_conf);+ return make_midi_in(c2, api_conf, midi1::available_backends);+ }++ return {};+}++static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>+make_midi2_in(const ump_input_configuration& base_conf, const std::any& api_conf)+{+ if (!api_conf.has_value())+ {+ return make_midi2_in(base_conf);+ }+ else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))+ {+ if (*api_p == libremidi::API::UNSPECIFIED)+ {+ return make_midi2_in(base_conf);+ }+ else+ {+ return make_midi2_in(base_conf, midi_in_configuration_for(*api_p), *api_p);+ }+ }+ else+ {+ if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)+ return {};+ else+ return make_midi2_in(base_conf, api_conf, libremidi::midi_api(api_conf));+ }+}++/// MIDI 2 constructors+LIBREMIDI_INLINE midi_in::midi_in(ump_input_configuration base_conf) noexcept+ : impl_{make_midi2_in(base_conf)}+{+}++LIBREMIDI_INLINE+midi_in::midi_in(ump_input_configuration base_conf, std::any api_conf)+ : impl_{make_midi2_in(base_conf, api_conf)}+{+ if (!impl_)+ {+ error_handler e;+ e.libremidi_handle_error(base_conf, "Could not open midi in for the given api");+ impl_ = std::make_unique<midi_in_dummy>(input_configuration{}, dummy_configuration{});+ }+}++LIBREMIDI_INLINE midi_in::~midi_in() = default;++LIBREMIDI_INLINE midi_in::midi_in(midi_in&& other) noexcept+ : impl_{std::move(other.impl_)}+{+ other.impl_+ = std::make_unique<libremidi::midi_in_dummy>(input_configuration{}, dummy_configuration{});+}++LIBREMIDI_INLINE+stdx::error midi_in::set_port_name(std::string_view portName)+{+ if(impl_->is_port_open())+ return impl_->set_port_name(portName);++ return std::errc::not_connected;+}++LIBREMIDI_INLINE midi_in& midi_in::operator=(midi_in&& other) noexcept+{+ this->impl_ = std::move(other.impl_);+ other.impl_+ = std::make_unique<libremidi::midi_in_dummy>(input_configuration{}, dummy_configuration{});+ return *this;+}++LIBREMIDI_INLINE+libremidi::API midi_in::get_current_api() const noexcept+{+ return impl_->get_current_api();+}++LIBREMIDI_INLINE+stdx::error midi_in::open_port(const input_port& port, std::string_view portName)+{+ if (auto err = impl_->is_client_open(); err != stdx::error{})+ return std::errc::not_connected;++ if (impl_->is_port_open())+ return std::errc::operation_not_supported;++ auto ret = impl_->open_port(port, portName);+ if (ret == stdx::error{})+ {+ impl_->connected_ = true;+ impl_->port_open_ = true;+ }+ return ret;+}++LIBREMIDI_INLINE+stdx::error midi_in::open_virtual_port(std::string_view portName)+{+ if (auto err = impl_->is_client_open(); err != stdx::error{})+ return std::errc::not_connected;++ if (impl_->is_port_open())+ return std::errc::operation_not_supported;++ auto ret = impl_->open_virtual_port(portName);+ if (ret == stdx::error{})+ impl_->port_open_ = true;+ return ret;+}++LIBREMIDI_INLINE+stdx::error midi_in::close_port()+{+ if (auto err = impl_->is_client_open(); err != stdx::error{})+ return std::errc::not_connected;++ auto ret = impl_->close_port();++ impl_->connected_ = false;+ impl_->port_open_ = false;++ return ret;+}++LIBREMIDI_INLINE+bool midi_in::is_port_open() const noexcept+{+ return impl_->is_port_open();+}++LIBREMIDI_INLINE+bool midi_in::is_port_connected() const noexcept+{+ return impl_->is_port_connected();+}++LIBREMIDI_INLINE+int64_t midi_in::absolute_timestamp() const noexcept+{+ return impl_->absolute_timestamp();+}+}
+ vendor/libremidi/midi_out.cpp view
@@ -0,0 +1,326 @@+#if !defined(LIBREMIDI_HEADER_ONLY)+ #include <libremidi/libremidi.hpp>+#endif++#include <libremidi/backends.hpp>+#include <libremidi/detail/midi_api.hpp>++#include <array>+#include <cassert>++namespace libremidi+{+static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>+make_midi_out_impl(auto base_conf, std::any api_conf)+{+ std::unique_ptr<midi_out_api> ptr;+ auto from_api = [&]<typename T>(T& /*backend*/) mutable {+ if (auto conf = std::any_cast<typename T::midi_out_configuration>(&api_conf))+ {+ ptr = libremidi::make<typename T::midi_out>(std::move(base_conf), std::move(*conf));+ return true;+ }+ return false;+ };+ std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi1::available_backends);+ std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi2::available_backends);+ return ptr;+}++static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>+make_midi_out(const output_configuration& base_conf)+{+ for (const auto& api : available_apis())+ {+ try+ {+ if (auto ret = make_midi_out_impl(base_conf, midi_out_configuration_for(api)))+ return ret;+ }+ catch (const std::exception& e)+ {+ }+ }++ for (const auto& api : available_ump_apis())+ {+ try+ {+ if (auto ret = make_midi_out_impl(base_conf, midi_out_configuration_for(api)))+ return ret;+ }+ catch (const std::exception& e)+ {+ }+ }++ return std::make_unique<midi_out_dummy>(output_configuration{}, dummy_configuration{});+}++static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>+make_midi_out(const output_configuration& base_conf, const std::any& api_conf)+{+ if (!api_conf.has_value())+ {+ return make_midi_out(base_conf);+ }+ else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))+ {+ if (*api_p == libremidi::API::UNSPECIFIED)+ {+ return make_midi_out(base_conf);+ }+ else+ {+ return make_midi_out_impl(base_conf, midi_out_configuration_for(*api_p));+ }+ }+ else+ {+ if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)+ return make_midi_out(base_conf);+ else+ return make_midi_out_impl(base_conf, api_conf);+ }+}++LIBREMIDI_INLINE midi_out::midi_out(const output_configuration& base_conf) noexcept+ : impl_{make_midi_out(base_conf)}+{+}++LIBREMIDI_INLINE+midi_out::midi_out(output_configuration base_conf, std::any api_conf)+ : impl_{make_midi_out(base_conf, api_conf)}+{+ if (!impl_)+ {+ error_handler e;+ e.libremidi_handle_error(base_conf, "Could not open midi out for the given api");+ impl_ = std::make_unique<midi_out_dummy>(output_configuration{}, dummy_configuration{});+ }+}++LIBREMIDI_INLINE midi_out::~midi_out() = default;++LIBREMIDI_INLINE midi_out::midi_out(midi_out&& other) noexcept+ : impl_{std::move(other.impl_)}+{+ other.impl_+ = std::make_unique<libremidi::midi_out_dummy>(output_configuration{}, dummy_configuration{});+}++LIBREMIDI_INLINE midi_out& midi_out::operator=(midi_out&& other) noexcept+{+ this->impl_ = std::move(other.impl_);+ other.impl_+ = std::make_unique<libremidi::midi_out_dummy>(output_configuration{}, dummy_configuration{});+ return *this;+}++LIBREMIDI_INLINE+stdx::error midi_out::set_port_name(std::string_view portName) const+{+ if(impl_->is_port_open())+ return impl_->set_port_name(portName);++ return std::errc::not_connected;+}++LIBREMIDI_INLINE+libremidi::API midi_out::get_current_api() const noexcept+{+ return impl_->get_current_api();+}++LIBREMIDI_INLINE+stdx::error midi_out::open_port(const output_port& port, std::string_view portName) const+{+ if (auto err = impl_->is_client_open(); err != stdx::error{})+ return std::errc::not_connected;++ if (impl_->is_port_open())+ return std::errc::operation_not_supported;++ auto ret = impl_->open_port(port, portName);+ if (ret == stdx::error{})+ {+ impl_->connected_ = true;+ impl_->port_open_ = true;+ }+ return ret;+}++LIBREMIDI_INLINE+stdx::error midi_out::open_virtual_port(std::string_view portName) const+{+ if (auto err = impl_->is_client_open(); err != stdx::error{})+ return std::errc::not_connected;++ if (impl_->is_port_open())+ return std::errc::operation_not_supported;++ auto ret = impl_->open_virtual_port(portName);+ if (ret == stdx::error{})+ impl_->port_open_ = true;+ return ret;+}++LIBREMIDI_INLINE+stdx::error midi_out::close_port() const+{+ if (auto err = impl_->is_client_open(); err != stdx::error{})+ return std::errc::not_connected;++ auto ret = impl_->close_port();+ impl_->connected_ = false;+ impl_->port_open_ = false;+ return ret;+}++LIBREMIDI_INLINE+bool midi_out::is_port_open() const noexcept+{+ return impl_->is_port_open();+}++LIBREMIDI_INLINE+bool midi_out::is_port_connected() const noexcept+{+ return impl_->is_port_connected();+}++LIBREMIDI_INLINE+stdx::error midi_out::send_message(const libremidi::message& message) const+{+ return send_message(message.bytes.data(), message.bytes.size());+}++LIBREMIDI_INLINE+stdx::error midi_out::send_message(std::span<const unsigned char> message) const+{+ return send_message(message.data(), message.size());+}++LIBREMIDI_INLINE+stdx::error midi_out::send_message(unsigned char b0) const+{+ return send_message(&b0, 1);+}++LIBREMIDI_INLINE+stdx::error midi_out::send_message(unsigned char b0, unsigned char b1) const+{+ return send_message(std::to_array({b0, b1}));+}++LIBREMIDI_INLINE+stdx::error midi_out::send_message(unsigned char b0, unsigned char b1, unsigned char b2) const+{+ return send_message(std::to_array({b0, b1, b2}));+}++LIBREMIDI_INLINE+stdx::error midi_out::send_message(const unsigned char* message, size_t size) const+{+#if defined(LIBREMIDI_ASSERTIONS)+ assert(size > 0);+#endif++ return impl_->send_message(message, size);+}++LIBREMIDI_INLINE+int64_t midi_out::current_time()+{+ return impl_->current_time();+}++LIBREMIDI_INLINE+stdx::error midi_out::schedule_message(int64_t ts, const unsigned char* message, size_t size) const+{+#if defined(LIBREMIDI_ASSERTIONS)+ assert(size > 0);+#endif++ return impl_->schedule_message(ts, message, size);+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(const uint32_t* message, size_t size) const+{+#if defined(LIBREMIDI_ASSERTIONS)+ assert(size > 0);+ assert(size <= 4);+#endif++ return impl_->send_ump(message, size);+}+LIBREMIDI_INLINE+stdx::error midi_out::send_ump(const libremidi::ump& message) const+{+ return send_ump(message.data, message.size());+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(std::span<const uint32_t> message) const+{+ return send_ump(message.data(), message.size());+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(uint32_t b0) const+{+ return send_ump(&b0, 1);+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(uint32_t b0, uint32_t b1) const+{+ return send_ump(std::to_array({b0, b1}));+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(uint32_t b0, uint32_t b1, uint32_t b2) const+{+ return send_ump(std::to_array({b0, b1, b2}));+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3) const+{+ return send_ump(std::to_array({b0, b1, b2, b3}));+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(int32_t b0) const+{+ return send_ump(reinterpret_cast<uint32_t*>(&b0), 1);+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(int64_t b01) const+{+ auto ptr = reinterpret_cast<uint32_t*>(&b01);+ uint32_t msg[2]{ptr[1], ptr[0]};+ return send_ump(msg, 2);+}++LIBREMIDI_INLINE+stdx::error midi_out::send_ump(uint64_t b01) const+{+ auto ptr = reinterpret_cast<uint32_t*>(&b01);+ uint32_t msg[2]{ptr[1], ptr[0]};+ return send_ump(msg, 2);+}++LIBREMIDI_INLINE+stdx::error midi_out::schedule_ump(int64_t ts, const uint32_t* message, size_t size) const+{+#if defined(LIBREMIDI_ASSERTIONS)+ assert(size > 0);+#endif++ return impl_->schedule_ump(ts, message, size);+}+}
+ vendor/libremidi/observer.cpp view
@@ -0,0 +1,133 @@+#if !defined(LIBREMIDI_HEADER_ONLY)+ #include <libremidi/libremidi.hpp>+#endif++#include <libremidi/backends.hpp>+#include <libremidi/detail/midi_api.hpp>++namespace libremidi+{++static LIBREMIDI_INLINE std::unique_ptr<observer_api> make_observer(auto base_conf)+{+ for (const auto& api : available_apis())+ {+ try+ {+ if (auto impl_ = make_observer(base_conf, observer_configuration_for(api)))+ return impl_;+ }+ catch (const std::exception& e)+ {+ }+ }++ for (const auto& api : available_ump_apis())+ {+ try+ {+ if (auto impl_ = make_observer(base_conf, observer_configuration_for(api)))+ return impl_;+ }+ catch (const std::exception& e)+ {+ }+ }++ return std::make_unique<observer_dummy>(observer_configuration{}, dummy_configuration{});+}++LIBREMIDI_INLINE auto make_observer_impl(auto base_conf, std::any api_conf)+{+ std::unique_ptr<observer_api> ptr;+ auto from_api = [&]<typename T>(T& /*backend*/) mutable {+ if (auto conf = std::any_cast<typename T::midi_observer_configuration>(&api_conf))+ {+ ptr = libremidi::make<typename T::midi_observer>(std::move(base_conf), std::move(*conf));+ return true;+ }+ return false;+ };+ std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi1::available_backends);+ std::apply([&](auto&&... b) { (from_api(b) || ...); }, midi2::available_backends);+ return ptr;+}++LIBREMIDI_INLINE std::unique_ptr<observer_api> make_observer(auto base_conf, std::any api_conf)+{+ if (!api_conf.has_value())+ {+ return make_observer(base_conf);+ }+ else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))+ {+ if (*api_p == libremidi::API::UNSPECIFIED)+ {+ return make_observer(base_conf);+ }+ else+ {+ return make_observer_impl(base_conf, observer_configuration_for(*api_p));+ }+ }+ else+ {+ if (auto api = libremidi::midi_api(api_conf); api == libremidi::API::UNSPECIFIED)+ return {};+ else+ return make_observer_impl(base_conf, api_conf);+ }+}++LIBREMIDI_INLINE observer::observer(const observer_configuration& base_conf) noexcept+ : impl_{make_observer(base_conf)}+{+}++LIBREMIDI_INLINE observer::observer(observer_configuration base_conf, std::any api_conf)+ : impl_{make_observer(base_conf, api_conf)}+{+ if (!impl_)+ {+ error_handler e;+ e.libremidi_handle_error(base_conf, "Could not open observer for the given api");+ impl_ = std::make_unique<observer_dummy>(observer_configuration{}, dummy_configuration{});+ }+}++LIBREMIDI_INLINE observer::observer(observer&& other) noexcept+ : impl_{std::move(other.impl_)}+{+ other.impl_ = std::make_unique<libremidi::observer_dummy>(+ observer_configuration{}, dummy_configuration{});+}++LIBREMIDI_INLINE observer& observer::operator=(observer&& other) noexcept+{+ this->impl_ = std::move(other.impl_);+ other.impl_ = std::make_unique<libremidi::observer_dummy>(+ observer_configuration{}, dummy_configuration{});+ return *this;+}++LIBREMIDI_INLINE+observer::~observer() = default;++LIBREMIDI_INLINE+libremidi::API observer::get_current_api() const noexcept+{+ return impl_->get_current_api();+}++LIBREMIDI_INLINE+std::vector<libremidi::input_port> observer::get_input_ports() const noexcept+{+ return impl_->get_input_ports();+}++LIBREMIDI_INLINE+std::vector<libremidi::output_port> observer::get_output_ports() const noexcept+{+ return impl_->get_output_ports();+}+}
+ vendor/libremidi/reader.cpp view
@@ -0,0 +1,752 @@+/*+Copyright (c) 2015, Dimitri Diakopoulos All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++* Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#if !defined(LIBREMIDI_HEADER_ONLY)+ #include <libremidi/reader.hpp>+#endif+#include <libremidi/message.hpp>++#include <algorithm>+#include <iostream>++// File Parsing Validation Todo:+// ==============================+// [] Bad file name+// [] Bad header+// [] Unknown header type+// [] Bad header size+// [] Bad type+// [] Bad tmecode+// [] Header too short+// [] Track too short+// [] Event too short+// ==============================++#if defined(__LIBREMIDI_DEBUG__)+std::ostream& operator<<(std::ostream& s, const libremidi::message& m)+{+ s << "[ MIDI: ";+ for (auto b : m)+ s << (unsigned int)b << ' ';+ s << "]\n";+ return s;+}+#endif++namespace libremidi+{+namespace util+{+struct no_validator+{+ static inline bool validate_track([[maybe_unused]] const midi_track& track) { return true; }+};++struct validator+{+ static inline bool validate_track(const midi_track& track)+ {+ if (track.empty())+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "empty track" << std::endl;+#endif+ return false;+ }++ // Ensure that there is a unique EOT at the end of the track+ auto it = std::find_if(track.begin(), track.end(), [](const libremidi::track_event& msg) {+ static const auto eot = meta_events::end_of_track();+ return msg.m.bytes == eot.bytes;+ });++ if (it == track.end())+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "track has no END OF TRACK" << std::endl;+#endif+ return false;+ }++ if (&it->m != &track.back().m)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << std::distance(it, track.end());+ std::cerr << "track does not end with END OF TRACK" << std::endl;+#endif+ return false;+ }++ return true;+ }+};++// Used when we know that we have enough space+struct read_unchecked+{+ // Read a MIDI-style variable-length integer (big-endian value in groups of 7 bits,+ // with top bit set to signify that another byte follows).+ static inline void ensure_size(+ [[maybe_unused]] const uint8_t* begin, [[maybe_unused]] const uint8_t* end,+ [[maybe_unused]] int64_t needed)+ {+ }++ static inline uint32_t+ read_variable_length(uint8_t const*& data, [[maybe_unused]] uint8_t const* end)+ {+ uint32_t result = 0;+ while (true)+ {+ uint8_t b = *data++;+ if (b & 0x80)+ {+ result += (b & 0x7F);+ result <<= 7;+ }+ else+ {+ return result + b; // b is the last byte+ }+ }+ }++ static inline void read_bytes(+ midi_bytes& buffer, uint8_t const*& data, [[maybe_unused]] const uint8_t* end,+ const std::size_t num)+ {+ buffer.reserve(buffer.size() + num);+ for (std::size_t i = 0; i < num; ++i)+ buffer.push_back(*data++);+ }++ static inline void read_bytes(+ midi_bytes& buffer, uint8_t const*& data, [[maybe_unused]] const uint8_t* end, const int num)+ {+ read_bytes(buffer, data, end, static_cast<std::size_t>(num));+ }++ static inline uint16_t read_uint16_be(uint8_t const*& data, [[maybe_unused]] const uint8_t* end)+ {+ uint16_t result = *data++ << 8u;+ result += *data++;+ return result;+ }++ static inline uint32_t read_uint24_be(uint8_t const*& data, [[maybe_unused]] const uint8_t* end)+ {+ uint32_t result = *data++ << 16u;+ result += static_cast<uint32_t>(*data++ << 8u);+ result += *data++;+ return result;+ }++ static inline uint32_t read_uint32_be(uint8_t const*& data, [[maybe_unused]] const uint8_t* end)+ {+ uint32_t result = *data++ << 24u;+ result += static_cast<uint32_t>(*data++ << 16u);+ result += static_cast<uint32_t>(*data++ << 8u);+ result += *data++;+ return result;+ }+};++// Used when we do not know if we have enough bytes and have to check before reading+struct read_checked+{+ // Read a MIDI-style variable-length integer (big-endian value in groups of 7 bits,+ // with top bit set to signify that another byte follows).+ static inline void+ ensure_size(const uint8_t* begin, const uint8_t* end, const std::size_t needed)+ {+ if (const auto available = static_cast<std::size_t>(end - begin); available < needed)+ throw std::runtime_error("MIDI reader: not enough data to process");+ }++ static inline std::size_t read_variable_length(uint8_t const*& data, uint8_t const* end)+ {+ std::size_t result = 0;+ while (true)+ {+ ensure_size(data, end, 1);+ uint8_t b = *data++;+ if (b & 0x80)+ {+ result += (b & 0x7F);+ result <<= 7;+ }+ else+ {+ return result + b; // b is the last byte+ }+ }+ }++ static inline void+ read_bytes(midi_bytes& buffer, uint8_t const*& data, uint8_t const* end, const std::size_t num)+ {+ ensure_size(data, end, num);+ read_unchecked::read_bytes(buffer, data, end, num);+ }++ static inline uint16_t read_uint16_be(uint8_t const*& data, uint8_t const* end)+ {+ ensure_size(data, end, 2);+ return read_unchecked::read_uint16_be(data, end);+ }++ static inline uint32_t read_uint24_be(uint8_t const*& data, uint8_t const* end)+ {+ ensure_size(data, end, 3);+ return read_unchecked::read_uint24_be(data, end);+ }++ static inline uint32_t read_uint32_be(uint8_t const*& data, uint8_t const* end)+ {+ ensure_size(data, end, 4);+ return read_unchecked::read_uint32_be(data, end);+ }+};+}++#if defined(LIBREMIDI_UNCHECKED)+using byte_reader = util::read_unchecked;+#else+using byte_reader = util::read_checked;+#endif++#if defined(LIBREMIDI_UNVALIDATED)+using validator = util::no_validator;+#else+using validator = util::validator;+#endif++LIBREMIDI_INLINE+track_event parse_event(+ int tick, int track, const uint8_t*& dataStart, const uint8_t* dataEnd,+ message_type lastEventTypeByte)+{+ byte_reader::ensure_size(dataStart, dataEnd, 1);+ auto type = static_cast<message_type>(*dataStart++);++ track_event event{tick, track, message{}};++ if ((static_cast<uint8_t>(type) & 0xF0) == 0xF0)+ {+ // Meta event+ if (static_cast<uint8_t>(type) == 0xFF)+ {+ byte_reader::ensure_size(dataStart, dataEnd, 1);+ auto subtype = static_cast<meta_event_type>(*dataStart++);++ event.m.bytes.reserve(3);+ event.m.bytes.push_back(static_cast<uint8_t>(type));+ event.m.bytes.push_back(static_cast<uint8_t>(subtype));++ uint32_t length = 0;+ // Here we read the meta-event length manually, as this way we can also put it into+ // event.m.bytes+ while (true)+ {+ byte_reader::ensure_size(dataStart, dataEnd, 1);+ uint8_t b = *dataStart++;+ event.m.bytes.push_back(b);+ if (b & 0x80)+ {+ const uint8_t byte = (b & 0x7F);++ length += byte;+ length <<= 7;+ }+ else+ {+ length += b; // b is the last byte+ break;+ }+ }++ switch (subtype)+ {+ case meta_event_type::SEQUENCE_NUMBER: {+ switch (length)+ {+ case 0:+ return event;+ case 2:+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, 2);+ return event;+ default:+ throw std::invalid_argument("Expected length for SEQUENCE_NUMBER event is 0 or 2");+ }+ }+ case meta_event_type::TEXT:+ case meta_event_type::COPYRIGHT:+ case meta_event_type::TRACK_NAME:+ case meta_event_type::INSTRUMENT:+ case meta_event_type::LYRIC:+ case meta_event_type::MARKER:+ case meta_event_type::CUE:+ case meta_event_type::PATCH_NAME:+ case meta_event_type::DEVICE_NAME: {+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }++ case meta_event_type::END_OF_TRACK: {+ if (length != 0)+ throw std::invalid_argument("Expected length for END_OF_TRACK event is 0");+ return event;+ }+ case meta_event_type::TEMPO_CHANGE: {+ if (length != 3)+ throw std::invalid_argument("Expected length for TEMPO_CHANGE event is 3");+ // event.m.bytes[3] = read_uint24_be(dataStart); // @dimitri TOFIX+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }+ case meta_event_type::SMPTE_OFFSET: {+ if (length != 5)+ throw std::invalid_argument("Expected length for SMPTE_OFFSET event is 5");+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ auto& b = event.m.bytes;++ uint8_t format = (b[3] & 0b01100000) >> 5;+ uint8_t h = (b[3] & 0b00011111);++ if (format > 3)+ throw std::invalid_argument("SMPTE_OFFSET has unknown format");++ int max = 0;+ switch (format)+ {+ case 0: // 24+ max = 24;+ break;+ case 1: // 25+ max = 25;+ break;+ case 2: // 29+ max = 29;+ break;+ case 3: // 30+ max = 30;+ break;+ default:+ break;+ }++ if (h >= 24 || b[4] >= 60 || b[5] >= 60 || b[6] >= max || b[7] >= 100)+ throw std::invalid_argument("SMPTE_OFFSET is out-of-23:59:59:xx:99 bounds");+ return event;+ }+ case meta_event_type::TIME_SIGNATURE: {+ if (length != 4)+ throw std::invalid_argument("Expected length for TIME_SIGNATURE event is 4");+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }+ case meta_event_type::KEY_SIGNATURE: {+ if (length != 2)+ throw std::invalid_argument("Expected length for KEY_SIGNATURE event is 2");+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ auto k = static_cast<int8_t>(event.m[3]);+ if (k < -7 || k > 7)+ throw std::invalid_argument("Invalid KEY_SIGNATURE");+ if (event.m[4] > 1)+ throw std::invalid_argument("Invalid KEY_SIGNATURE");+ return event;+ }+ case meta_event_type::PROPRIETARY: {+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }+ case meta_event_type::CHANNEL_PREFIX: {+ if (length != 1)+ throw std::invalid_argument("Expected length for CHANNEL_PREFIX event is 1");+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }+ case meta_event_type::MIDI_PORT: {+ if (length != 1)+ throw std::invalid_argument("Expected length for MIDI_PORT event is 1");+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }+ case meta_event_type::UNKNOWN:+ default: {+ // Unknown events?+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }+ }+ }++ else if (type == message_type::SYSTEM_EXCLUSIVE)+ {+ const auto length = byte_reader::read_variable_length(dataStart, dataEnd);+ event.m.bytes = {static_cast<uint8_t>(type)};+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }++ else if (type == message_type::EOX)+ {+ const auto length = byte_reader::read_variable_length(dataStart, dataEnd);+ byte_reader::read_bytes(event.m.bytes, dataStart, dataEnd, length);+ return event;+ }+ else+ {+ throw std::runtime_error("Unrecognised MIDI event type byte");+ }+ }++ // Channel events+ else+ {+ event.m.bytes.clear();++ // Running status...+ if ((static_cast<uint8_t>(type) & 0x80) == 0)+ {+ // Reuse lastEventTypeByte as the event type.+ // eventTypeByte is actually the first parameter+ event.m.bytes.push_back(static_cast<uint8_t>(lastEventTypeByte));+ event.m.bytes.push_back(static_cast<uint8_t>(type));+ type = lastEventTypeByte;+ }+ else+ {+ event.m.bytes.push_back(static_cast<uint8_t>(type));++ byte_reader::ensure_size(dataStart, dataEnd, 1);+ event.m.bytes.push_back(static_cast<uint8_t>(*dataStart++));+ lastEventTypeByte = type;+ }++ static constexpr auto validate = [](midi_bytes& b) {+ if (b[1] < 128 && b[2] < 128)+ return true;+ throw std::invalid_argument("MIDI message has arguments > 127");+ };++ switch (static_cast<message_type>(static_cast<uint8_t>(type) & 0xF0))+ {+ case message_type::NOTE_OFF:+ case message_type::NOTE_ON:+ case message_type::POLY_PRESSURE:+ case message_type::CONTROL_CHANGE:+ byte_reader::ensure_size(dataStart, dataEnd, 1);+ event.m.bytes.push_back(*dataStart++);+ validate(event.m.bytes);+ return event;+ case message_type::PROGRAM_CHANGE:+ if (event.m.bytes[1] >= 128)+ throw std::invalid_argument("MIDI PC has arguments > 127");+ return event;+ case message_type::AFTERTOUCH:+ if (event.m.bytes[1] >= 128)+ throw std::invalid_argument("MIDI Atertouch has arguments > 127");+ return event;+ case message_type::PITCH_BEND:+ byte_reader::ensure_size(dataStart, dataEnd, 1);+ event.m.bytes.push_back(*dataStart++);+ validate(event.m.bytes);+ return event;++ case message_type::TIME_CODE:+ throw std::runtime_error("Unsupported MIDI event type TIME_CODE");+ case message_type::SONG_POS_POINTER:+ throw std::runtime_error("Unsupported MIDI event type SONG_POS_POINTER");+ case message_type::SONG_SELECT:+ throw std::runtime_error("Unsupported MIDI event type SONG_SELECT");+ case message_type::RESERVED1:+ throw std::runtime_error("Unsupported MIDI event type RESERVED1");+ case message_type::RESERVED2:+ throw std::runtime_error("Unsupported MIDI event type RESERVED2");+ case message_type::TUNE_REQUEST:+ throw std::runtime_error("Unsupported MIDI event type TUNE_REQUEST");+ case message_type::EOX:+ throw std::runtime_error("Unsupported MIDI event type EOX");+ // System Realtime Messages :+ case message_type::TIME_CLOCK:+ throw std::runtime_error("Unsupported MIDI event type TIME_CLOCK");+ case message_type::RESERVED3:+ throw std::runtime_error("Unsupported MIDI event type RESERVED3");+ case message_type::START:+ throw std::runtime_error("Unsupported MIDI event type START");+ case message_type::CONTINUE:+ throw std::runtime_error("Unsupported MIDI event type CONTINUE");+ case message_type::STOP:+ throw std::runtime_error("Unsupported MIDI event type STOP");+ case message_type::RESERVED4:+ throw std::runtime_error("Unsupported MIDI event type RESERVED4");+ case message_type::ACTIVE_SENSING:+ throw std::runtime_error("Unsupported MIDI event type ACTIVE_SENSING");+ case message_type::SYSTEM_RESET:+ throw std::runtime_error("Unsupported MIDI event type SYSTEM_RESET");+ case message_type::INVALID:+ throw std::runtime_error("Unsupported MIDI event type INVALID");+ case message_type::SYSTEM_EXCLUSIVE:+ throw std::runtime_error("Unsupported MIDI event type SYSTEM_EXCLUSIVE");+ default:+ throw std::runtime_error("Unsupported MIDI event type");+ }+ }+}++LIBREMIDI_INLINE+reader::reader(bool useAbsolute)+ : ticksPerBeat(480)+ , startingTempo(120)+ , useAbsoluteTicks(useAbsolute)+{+}++LIBREMIDI_INLINE+reader::~reader() { }++constexpr int str_to_headerid(const char* str)+{+ return str[0] << 24 | str[1] << 16 | str[2] << 8 | str[3];+}++LIBREMIDI_INLINE+auto reader::parse(const uint8_t* dataPtr, std::size_t size) noexcept -> parse_result+try+{+ using namespace libremidi::util;++ tracks.clear();++ if (size == 0)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "empty buffer passed to parse." << std::endl;+#endif+ return parse_result::invalid;+ }++ const uint8_t* const dataEnd = dataPtr + size;++ uint32_t headerId = read_checked::read_uint32_be(dataPtr, dataEnd);+ uint32_t headerLength = read_checked::read_uint32_be(dataPtr, dataEnd);++ if (static_cast<int>(headerId) != str_to_headerid("MThd") || headerLength != 6)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "couldn't parse header" << std::endl;+#endif+ return parse_result::invalid;+ }++ format = read_checked::read_uint16_be(+ dataPtr, dataEnd); //@tofix format type -> save for later eventually+ if (format > 2)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "unknown format" << std::endl;+#endif+ return parse_result::invalid;+ }+ int trackCount = read_checked::read_uint16_be(dataPtr, dataEnd);+ uint16_t timeDivision = read_checked::read_uint16_be(dataPtr, dataEnd);++ // CBB: deal with the SMPTE style time coding+ // timeDivision is described here http://www.sonicspot.com/guide/midifiles.html+ if (timeDivision & 0x8000)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "found SMPTE time frames (unsupported)" << std::endl;+ int fps = (timeDivision >> 16) & 0x7f;+ if (fps != -30 && fps != -29 && fps != -25 && fps != -24)+ return parse_result::invalid;+ int ticksPerFrame = timeDivision & 0xff;+#endif+ // given beats per second, timeDivision should be derivable.+ return parse_result::invalid;+ }++ startingTempo = 120.0f; // midi default+ ticksPerBeat = float(timeDivision); // ticks per beat (a beat is defined as a quarter note)++ parse_result result = parse_result::validated;++ for (int i = 0; i < trackCount; ++i)+ {+ midi_track track;++ headerId = read_checked::read_uint32_be(dataPtr, dataEnd);+ headerLength = read_checked::read_uint32_be(dataPtr, dataEnd);++ if (headerId != str_to_headerid("MTrk"))+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "couldn't find track header" << std::endl;+#endif+ return parse_result::incomplete;+ }++ int64_t available = dataEnd - dataPtr;+ if (available < headerLength)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "not enough data available" << std::endl;+#endif+ return parse_result::incomplete;+ }++ track.reserve(headerLength / 3);++ const uint8_t* const trackEnd = dataPtr + headerLength;++ auto runningEvent = message_type::INVALID;++ std::size_t tickCount = 0;++ while (dataPtr < trackEnd)+ {+ const auto tick = read_checked::read_variable_length(dataPtr, trackEnd);+ if (useAbsoluteTicks)+ {+ tickCount += tick;+ }+ else+ {+ tickCount = tick;+ }++ try+ {+ track_event ev+ = parse_event(static_cast<int>(tickCount), i, dataPtr, trackEnd, runningEvent);+ if (!ev.m.empty())+ {+ if (!ev.m.is_meta_event())+ {+ runningEvent = static_cast<message_type>(ev.m.bytes[0]);+ }+ }+ else+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "could not read event" << std::endl;+#endif+ dataPtr = trackEnd;+ result = parse_result::incomplete;+ continue;+ }++ track.push_back(std::move(ev));+ }+ catch (const std::exception& e)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "" << e.what() << std::endl;+#endif+ dataPtr = trackEnd;+ result = parse_result::incomplete;+ continue;+ }+ }++ if (result == parse_result::validated)+ {+ if (!validator::validate_track(track))+ {+ result = parse_result::complete;+ }+ }+ tracks.push_back(std::move(track));+ }++ if (result == parse_result::validated)+ {+ if (dataPtr != dataEnd)+ {+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "midifile has junk at end: " << std::intptr_t(dataEnd - dataPtr) << std::endl;+#endif+ result = parse_result::complete;+ }+ }+ return result;+}+catch (const std::exception& e)+{+#if defined(__LIBREMIDI_DEBUG__)+ std::cerr << "" << e.what() << std::endl;+#endif+ return parse_result::invalid;+}++// In ticks+LIBREMIDI_INLINE+double reader::get_end_time() const noexcept+{+ if (useAbsoluteTicks)+ {+ double totalLength = 0.;+ for (const auto& t : tracks)+ {+ if (!t.empty())+ {+ const auto& last_event = t.back();+ if (last_event.tick > totalLength)+ totalLength = last_event.tick;+ }+ }+ return totalLength;+ }+ else+ {+ double totalLength = 0.;+ for (const auto& t : tracks)+ {+ double trackLength = 0.;+ for (const auto& e : t)+ trackLength += e.tick;++ if (trackLength > totalLength)+ totalLength = trackLength;+ }+ return totalLength;+ }+}++LIBREMIDI_INLINE+auto reader::parse(const std::vector<uint8_t>& buffer) noexcept -> parse_result+{+ return parse(buffer.data(), buffer.size());+}++#if defined(LIBREMIDI_HAS_SPAN)+LIBREMIDI_INLINE+auto reader::parse(std::span<uint8_t> buffer) noexcept -> parse_result+{+ return parse(buffer.data(), buffer.size());+}+#endif+}
+ vendor/libremidi/writer.cpp view
@@ -0,0 +1,213 @@+/*+Copyright (c) 2015, Dimitri Diakopoulos All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++* Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*/++#if !defined(LIBREMIDI_HEADER_ONLY)+ #include <libremidi/writer.hpp>+#endif+#include <algorithm>+#include <bit>+#include <ostream>+#include <string>++#if !defined(__cpp_lib_bit_cast)+namespace std+{+template <typename R, typename T>+[[nodiscard]]+constexpr R bit_cast(const T& v) noexcept+{+ union+ {+ R res;+ T init;+ } u{.init = v};+ return u.res;+}+}+#endif+namespace libremidi+{+namespace util+{+template <typename T>+static LIBREMIDI_INLINE std::ostream& write_be(std::ostream& out, T value)+{+ static_assert(+ std::endian::native == std::endian::big || std::endian::native == std::endian::little);+ if constexpr (std::endian::native == std::endian::big)+ {+ out << value;+ }+ else+ {+ static constexpr auto N = sizeof(value);+ struct storage+ {+ uint8_t bytes[N];+ };+ auto data = std::bit_cast<storage>(value);+ std::reverse(data.bytes, data.bytes + N);+ out.write(reinterpret_cast<const char*>(data.bytes), static_cast<std::streamsize>(N));+ }+ return out;+}++// Write a number to the midifile+// as a variable length value which segments a file into 7-bit+// values. Maximum size of aValue is 0x7fffffff+static LIBREMIDI_INLINE void write_variable_length(uint32_t aValue, std::vector<uint8_t>& outdata)+{+ uint8_t bytes[5] = {0};++ bytes[0] = static_cast<uint8_t>((aValue >> 28) & 0x7F); // most significant 5 bits+ bytes[1] = static_cast<uint8_t>((aValue >> 21) & 0x7F); // next largest 7 bits+ bytes[2] = static_cast<uint8_t>((aValue >> 14) & 0x7F);+ bytes[3] = static_cast<uint8_t>((aValue >> 7) & 0x7F);+ bytes[4] = static_cast<uint8_t>((aValue) & 0x7F); // least significant 7 bits++ int start = 0;+ while (start < 5 && bytes[start] == 0)+ start++;++ for (int i = start; i < 4; i++)+ {+ bytes[i] = bytes[i] | 0x80;+ outdata.push_back(bytes[i]);+ }+ outdata.push_back(bytes[4]);+}++static LIBREMIDI_INLINE void+add_event_track_count_check(std::vector<midi_track>& tracks, int track)+{+ if (track < 0)+ throw std::out_of_range("Refusing to add an event to track " + std::to_string(track) + ".");+ if (track > 65535)+ throw std::out_of_range(+ "Refusing to add an event to track " + std::to_string(track)+ + " ; change add_event_track_count_check in libremidi writer.cpp to increase the limit.");++ while (tracks.size() < static_cast<std::size_t>(track + 1)) // NOLINT(*-misplaced-widening-cast)+ tracks.emplace_back();+}+}++LIBREMIDI_INLINE+void writer::add_event(const int tick, const int track, const message& m)+{+ util::add_event_track_count_check(tracks, track);++ tracks[static_cast<std::size_t>(track)].push_back({tick, track, m});+}++LIBREMIDI_INLINE+void writer::add_event(int track, const track_event& m)+{+ util::add_event_track_count_check(tracks, track);++ tracks[static_cast<std::size_t>(track)].push_back(m);+}++LIBREMIDI_INLINE+void writer::add_track()+{+ util::add_event_track_count_check(tracks, static_cast<int>(tracks.size() + 1));+}++LIBREMIDI_INLINE+void writer::write(std::ostream& out) const+{+ // MIDI File Header+ out.write("MThd", 4);+ util::write_be<uint32_t>(out, 6);+ util::write_be<uint16_t>(out, (tracks.size() == 1) ? 0 : 1);+ util::write_be<uint16_t>(out, static_cast<uint16_t>(tracks.size()));+ util::write_be<uint16_t>(out, ticksPerQuarterNote);++ std::vector<uint8_t> trackRawData;+ for (const auto& event_list : tracks)+ {+ trackRawData.clear();+ // Rough estimation of the memory to allocate+ trackRawData.reserve(event_list.size() * 3);++ for (const auto& event : event_list)+ {+ const auto& msg = event.m;+ if (msg.empty())+ continue;++ // Suppress end-of-track meta messages (one will be added+ // automatically after all track data has been written).+ if (msg.get_meta_event_type() == meta_event_type::END_OF_TRACK)+ continue;++ util::write_variable_length(static_cast<uint32_t>(event.tick), trackRawData);++ if ((msg.get_message_type() == message_type::SYSTEM_EXCLUSIVE)+ || (event.m.get_message_type() == message_type::EOX))+ {+ // 0xf0 == Complete sysex message (0xf0 is part of the raw MIDI).+ // 0xf7 == Raw byte message (0xf7 not part of the raw MIDI).+ // Print the first byte of the message (0xf0 or 0xf7), then+ // print a VLV length for the rest of the bytes in the message.+ // In other words, when creating a 0xf0 or 0xf7 MIDI message,+ // do not insert the VLV byte length yourself, as this code will+ // do it for you automatically.+ trackRawData.emplace_back(msg.bytes[0]); // 0xf0 or 0xf7;++ util::write_variable_length(static_cast<uint32_t>(msg.size()) - 1, trackRawData);++ trackRawData.insert(+ trackRawData.end(), msg.bytes.data() + 1, msg.bytes.data() + msg.bytes.size());+ }+ else+ {+ // Non-sysex type of message, so just output the bytes of the message:+ trackRawData.insert(+ trackRawData.end(), msg.bytes.data(), msg.bytes.data() + msg.bytes.size());+ }+ }++ auto size = trackRawData.size();+ const auto eot = meta_events::end_of_track();++ if ((size < 3) || !((trackRawData[size - 3] == 0xFF) && (trackRawData[size - 2] == 0x2F)))+ {+ trackRawData.emplace_back(0x0); // tick+ trackRawData.emplace_back(eot[0]);+ trackRawData.emplace_back(eot[1]);+ trackRawData.emplace_back(eot[2]);+ }++ // Write the track ID marker "MTrk":+ out.write("MTrk", 4);+ util::write_be<uint32_t>(out, static_cast<uint32_t>(trackRawData.size()));+ out.write(+ reinterpret_cast<char*>(trackRawData.data()),+ static_cast<std::streamsize>(trackRawData.size()));+ }+}+}