Program Listing for File libremidi.cpp

Return to documentation for file (include/libremidi/libremidi.cpp)

#include <cmath>
#include <sstream>
#include <thread>

#if !defined(LIBREMIDI_HEADER_ONLY)
#  include <libremidi/libremidi.hpp>
#endif

#include <libremidi/detail/midi_api.hpp>
#if !__has_include(<weak_libjack.h>) && !__has_include(<jack/jack.h>)
#  if defined(LIBREMIDI_JACK)
#    undef LIBREMIDI_JACK
#  endif
#endif
#if !defined(LIBREMIDI_ALSA) && !defined(LIBREMIDI_JACK) && !defined(LIBREMIDI_COREAUDIO) \
    && !defined(LIBREMIDI_WINMM)
#  define LIBREMIDI_DUMMY
#endif

#if defined(LIBREMIDI_ALSA)
#  include <libremidi/detail/alsa.hpp>
#  include <libremidi/detail/raw_alsa.hpp>
#endif

#if defined(LIBREMIDI_JACK)
#  include <libremidi/detail/jack.hpp>
#endif

#if defined(LIBREMIDI_COREAUDIO)
#  include <libremidi/detail/coreaudio.hpp>
#endif

#if defined(LIBREMIDI_WINMM)
#  include <libremidi/detail/winmm.hpp>
#endif

#if defined(LIBREMIDI_WINUWP)
#  include <libremidi/detail/winuwp.hpp>
#endif

#if defined(LIBREMIDI_EMSCRIPTEN)
#  include <libremidi/detail/emscripten.hpp>
#endif

#if defined(LIBREMIDI_DUMMY)
#  include <libremidi/detail/dummy.hpp>
#endif

namespace libremidi
{

// The order here will control the order of the API search in
// the constructor.
template <typename unused, typename... Args>
constexpr auto make_tl(unused, Args...)
{
  return std::tuple<Args...>{};
}
static constexpr auto available_backends = make_tl(
    0
#if defined(LIBREMIDI_ALSA)
      , raw_alsa_backend {}
    , alsa_backend {}
#endif
#if defined(LIBREMIDI_COREAUDIO)
    , core_backend {}
#endif
#if defined(LIBREMIDI_JACK)
    , jack_backend {}
#endif
#if defined(LIBREMIDI_WINMM)
    , winmm_backend {}
#endif
#if defined(LIBREMIDI_WINUWP)
    , winuwp_backend {}
#endif
#if defined(LIBREMIDI_EMSCRIPTEN)
    , emscripten_backend {}
#endif
#if defined(LIBREMIDI_DUMMY)
    , dummy_backend {}
#endif
);

// There should always be at least one back-end.
static_assert(std::tuple_size_v<decltype(available_backends)> >= 1);

template <typename F>
auto for_all_backends(F&& f)
{
  std::apply([&](auto&&... x) { (f(x), ...); }, available_backends);
}

template <typename F>
auto for_backend(libremidi::API api, F&& f)
{
  for_all_backends([&](auto b) {
    if (b.API == api)
      f(b);
  });
}

LIBREMIDI_INLINE midi_exception::~midi_exception() = default;
LIBREMIDI_INLINE no_devices_found_error::~no_devices_found_error() = default;
LIBREMIDI_INLINE invalid_device_error::~invalid_device_error() = default;
LIBREMIDI_INLINE memory_error::~memory_error() = default;
LIBREMIDI_INLINE invalid_parameter_error::~invalid_parameter_error() = default;
LIBREMIDI_INLINE invalid_use_error::~invalid_use_error() = default;
LIBREMIDI_INLINE driver_error::~driver_error() = default;
LIBREMIDI_INLINE system_error::~system_error() = default;
LIBREMIDI_INLINE thread_error::~thread_error() = default;

LIBREMIDI_INLINE midi_in::~midi_in() = default;
LIBREMIDI_INLINE midi_out::~midi_out() = default;

LIBREMIDI_INLINE
bool chunking_parameters::default_wait(std::chrono::microseconds time_to_wait, int written_bytes) {
  std::this_thread::sleep_for(time_to_wait);
  return true;
}

[[nodiscard]] LIBREMIDI_INLINE std::vector<libremidi::API> available_apis() noexcept
{
  std::vector<libremidi::API> apis;
  for_all_backends([&](auto b) { apis.push_back(b.API); });
  return apis;
}

[[nodiscard]] LIBREMIDI_INLINE std::unique_ptr<observer_api>
open_midi_observer(libremidi::API api, observer::callbacks&& cb)
{
  std::unique_ptr<observer_api> ptr;

  for_backend(api, [&](auto b) {
    ptr = std::make_unique<typename decltype(b)::midi_observer>(std::move(cb));
  });

  return ptr;
}

[[nodiscard]] LIBREMIDI_INLINE std::unique_ptr<midi_in_api>
open_midi_in(libremidi::API api, std::string_view clientName, unsigned int queueSizeLimit)
{
  std::unique_ptr<midi_in_api> ptr;

  for_backend(api, [&](auto b) {
    ptr = std::make_unique<typename decltype(b)::midi_in>(clientName, queueSizeLimit);
  });

  return ptr;
}

[[nodiscard]] LIBREMIDI_INLINE std::unique_ptr<midi_out_api>
open_midi_out(libremidi::API api, std::string_view clientName)
{

  std::unique_ptr<midi_out_api> ptr;

  for_backend(
      api, [&](auto b) { ptr = std::make_unique<typename decltype(b)::midi_out>(clientName); });

  return ptr;
}

LIBREMIDI_INLINE observer::observer(libremidi::API api, observer::callbacks cbs)
    : impl_{open_midi_observer(api, std::move(cbs))}
{
}

LIBREMIDI_INLINE
observer::~observer() = default;

LIBREMIDI_INLINE
libremidi::API midi_in::get_current_api() const noexcept
{
  return rtapi_->get_current_api();
}

LIBREMIDI_INLINE
void midi_in::open_port(unsigned int portNumber, std::string_view portName)
{
  rtapi_->open_port(portNumber, portName);
}

LIBREMIDI_INLINE
void midi_in::open_virtual_port(std::string_view portName)
{
  rtapi_->open_virtual_port(portName);
}

LIBREMIDI_INLINE
void midi_in::close_port()
{
  rtapi_->close_port();
}

LIBREMIDI_INLINE
bool midi_in::is_port_open() const noexcept
{
  return rtapi_->is_port_open();
}

LIBREMIDI_INLINE
void midi_in::set_callback(message_callback callback)
{
  (static_cast<midi_in_api*>(rtapi_.get()))->set_callback(std::move(callback));
}

LIBREMIDI_INLINE
void midi_in::cancel_callback()
{
  (static_cast<midi_in_api*>(rtapi_.get()))->cancel_callback();
}

LIBREMIDI_INLINE
unsigned int midi_in::get_port_count()
{
  return rtapi_->get_port_count();
}

LIBREMIDI_INLINE
std::string midi_in::get_port_name(unsigned int portNumber)
{
  return rtapi_->get_port_name(portNumber);
}

LIBREMIDI_INLINE
void midi_in::ignore_types(bool midiSysex, bool midiTime, bool midiSense)
{
  (static_cast<midi_in_api*>(rtapi_.get()))->ignore_types(midiSysex, midiTime, midiSense);
}

LIBREMIDI_INLINE
message midi_in::get_message()
{
  return (static_cast<midi_in_api*>(rtapi_.get()))->get_message();
}

LIBREMIDI_INLINE
bool midi_in::get_message(message& msg)
{
  return (static_cast<midi_in_api*>(rtapi_.get()))->get_message(msg);
}

LIBREMIDI_INLINE
void midi_in::set_error_callback(midi_error_callback errorCallback)
{
  rtapi_->set_error_callback(std::move(errorCallback));
}

LIBREMIDI_INLINE
libremidi::API midi_out::get_current_api() noexcept
{
  return rtapi_->get_current_api();
}

LIBREMIDI_INLINE
void midi_out::open_port(unsigned int portNumber, std::string_view portName)
{
  rtapi_->open_port(portNumber, portName);
}

LIBREMIDI_INLINE
void midi_out::open_virtual_port(std::string_view portName)
{
  rtapi_->open_virtual_port(portName);
}

LIBREMIDI_INLINE
void midi_out::close_port()
{
  rtapi_->close_port();
}

LIBREMIDI_INLINE
bool midi_out::is_port_open() const noexcept
{
  return rtapi_->is_port_open();
}

LIBREMIDI_INLINE
unsigned int midi_out::get_port_count()
{
  return rtapi_->get_port_count();
}

LIBREMIDI_INLINE
std::string midi_out::get_port_name(unsigned int portNumber)
{
  return rtapi_->get_port_name(portNumber);
}

LIBREMIDI_INLINE
void midi_out::send_message(const std::vector<unsigned char>& message)
{
  send_message(message.data(), message.size());
}

LIBREMIDI_INLINE
void midi_out::send_message(const libremidi::message& message)
{
  send_message(message.bytes.data(), message.bytes.size());
}

#if LIBREMIDI_HAS_SPAN
LIBREMIDI_INLINE
void midi_out::send_message(std::span<unsigned char> message)
{
  send_message(message.data(), message.size());
}

#endif
LIBREMIDI_INLINE
void midi_out::send_message(const unsigned char* message, size_t size)
{
  (static_cast<midi_out_api*>(rtapi_.get()))->send_message(message, size);
}

LIBREMIDI_INLINE
void midi_out::set_error_callback(midi_error_callback errorCallback) noexcept
{
  rtapi_->set_error_callback(std::move(errorCallback));
}

LIBREMIDI_INLINE
std::string get_version() noexcept
{
  return std::string{LIBREMIDI_VERSION};
}

LIBREMIDI_INLINE
midi_in::midi_in(libremidi::API api, std::string_view clientName, unsigned int queueSizeLimit)
{
  if (api != libremidi::API::UNSPECIFIED)
  {
    // Attempt to open the specified API.
    if ((rtapi_ = open_midi_in(api, clientName, queueSizeLimit)))
    {
      return;
    }

    // No compiled support for specified API value.  Issue a warning
    // and continue as if no API was specified.
    std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl;
  }

  // Iterate through the compiled APIs and return as soon as we find
  // one with at least one port or we reach the end of the list.
  for (const auto& api : available_apis())
  {
    rtapi_ = open_midi_in(api, clientName, queueSizeLimit);
    if (rtapi_ && rtapi_->get_port_count() != 0)
    {
      break;
    }
  }

  if (rtapi_)
  {
    return;
  }
}

LIBREMIDI_INLINE
void midi_in::set_client_name(std::string_view clientName)
{
  rtapi_->set_client_name(clientName);
}

LIBREMIDI_INLINE
void midi_in::set_port_name(std::string_view portName)
{
  rtapi_->set_port_name(portName);
}

LIBREMIDI_INLINE
midi_out::midi_out(libremidi::API api, std::string_view clientName)
{
  if (api != libremidi::API::UNSPECIFIED)
  {
    // Attempt to open the specified API.
    rtapi_ = open_midi_out(api, clientName);
    if (rtapi_)
    {
      return;
    }

    // No compiled support for specified API value.  Issue a warning
    // and continue as if no API was specified.
    std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl;
  }

  // Iterate through the compiled APIs and return as soon as we find
  // one with at least one port or we reach the end of the list.
  for (const auto& api : available_apis())
  {
    rtapi_ = open_midi_out(api, clientName);
    if (rtapi_ && rtapi_->get_port_count() != 0)
    {
      break;
    }
  }

  if (rtapi_)
  {
    return;
  }

  // It should not be possible to get here because the preprocessor
  // definition LIBREMIDI_DUMMY is automatically defined if no
  // API-specific definitions are passed to the compiler. But just in
  // case something weird happens, we'll thrown an error.
  throw midi_exception{"RtMidiOut: no compiled API support found ... critical error!!"};
}

LIBREMIDI_INLINE
void midi_out::set_client_name(std::string_view clientName)
{
  rtapi_->set_client_name(clientName);
}

LIBREMIDI_INLINE
void midi_out::set_port_name(std::string_view portName)
{
  rtapi_->set_port_name(portName);
}

LIBREMIDI_INLINE
void midi_out::set_chunking_parameters(std::optional<chunking_parameters> parameters)
{
  rtapi_->set_chunking_parameters(std::move(parameters));
}
}