packages feed

libremidi 0.2.0 → 0.3.0

raw patch · 20 files changed

+2867/−2825 lines, 20 files

Files

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()));+  }+}+}