#pragma once

#include <libremidi/backends/linux/dylib_loader.hpp>

#include <alsa/asoundlib.h> // IWYU pragma: export

#if defined(SND_LIB_VERSION)
  #if __has_include(<alsa/rawmidi.h>) && SND_LIB_VERSION >= ((1 << 16) | (2 << 8) | 6)
    #define LIBREMIDI_ALSA_HAS_RAMWIDI 1
    #define LIBREMIDI_ALSA_HAS_RAWMIDI_TREAD 1
  #endif

  #if __has_include(<alsa/ump.h>) && SND_LIB_VERSION >= ((1 << 16) | (2 << 8) | 10)
    #define LIBREMIDI_ALSA_HAS_UMP 1
  #endif
#endif

namespace libremidi
{

struct libasound
{
  // Useful one-liner:
  // nm -A * | grep ' snd_' | grep -v '@' | cut -f 2 -d 'U' | sort | uniq  | sed 's/ snd_//' | sed 's/_/, /' | awk ' { print "LIBREMIDI_SYMBOL_DEF(snd_"$1 " " $2 ");" }'

  explicit libasound()
      : library{"libasound.so.2"}
  {
    if (!library)
    {
      available = false;
      return;
    }

    strerror = library.symbol<decltype(&::snd_strerror)>("snd_strerror");
    if (!strerror)
      available = false;
  }

  static const libasound& instance()
  {
    static const libasound self;
    return self;
  }

  dylib_loader library;
  decltype(&::snd_strerror) strerror{};
  bool available{true};

  struct card_t
  {
    explicit card_t(const dylib_loader& library)
    {
      if (!library)
      {
        available = false;
        return;
      }

      LIBREMIDI_SYMBOL_INIT(snd_card, get_name)
      LIBREMIDI_SYMBOL_INIT(snd_card, next)
    }
    bool available{true};

    LIBREMIDI_SYMBOL_DEF(snd_card, get_name)
    LIBREMIDI_SYMBOL_DEF(snd_card, next)
  } card{library};

  struct ctl_t
  {
    explicit ctl_t(const dylib_loader& library)
        : rawmidi{library}
#if LIBREMIDI_ALSA_HAS_UMP
        , ump{library}
#endif
    {
      if (!library)
      {
        available = false;
        return;
      }

      LIBREMIDI_SYMBOL_INIT(snd_ctl, close)
      LIBREMIDI_SYMBOL_INIT(snd_ctl, open)
    }
    bool available{true};

    LIBREMIDI_SYMBOL_DEF(snd_ctl, close)
    LIBREMIDI_SYMBOL_DEF(snd_ctl, open)

    struct rawmidi_t
    {
      explicit rawmidi_t(const dylib_loader& library)
      {
        if (!library)
        {
          available = false;
          return;
        }

        LIBREMIDI_SYMBOL_INIT(snd_ctl_rawmidi, info)
        LIBREMIDI_SYMBOL_INIT(snd_ctl_rawmidi, next_device)
      }
      bool available{true};
      LIBREMIDI_SYMBOL_DEF(snd_ctl_rawmidi, info)
      LIBREMIDI_SYMBOL_DEF(snd_ctl_rawmidi, next_device)
    } rawmidi;

#if LIBREMIDI_ALSA_HAS_UMP
    struct ump_t
    {
      explicit ump_t(const dylib_loader& library)
      {
        if (!library)
        {
          available = false;
          return;
        }

        LIBREMIDI_SYMBOL_INIT(snd_ctl_ump, block_info)
        LIBREMIDI_SYMBOL_INIT(snd_ctl_ump, endpoint_info)
        LIBREMIDI_SYMBOL_INIT(snd_ctl_ump, next_device)
      }
      bool available{true};
      LIBREMIDI_SYMBOL_DEF(snd_ctl_ump, block_info)
      LIBREMIDI_SYMBOL_DEF(snd_ctl_ump, endpoint_info)
      LIBREMIDI_SYMBOL_DEF(snd_ctl_ump, next_device)
    } ump;
#endif
  } ctl{library};

  struct midi_t
  {
    explicit midi_t(const dylib_loader& library)
    {
      if (!library)
      {
        available = false;
        return;
      }

      LIBREMIDI_SYMBOL_INIT(snd_midi, event_decode)
      LIBREMIDI_SYMBOL_INIT(snd_midi, event_encode)
      LIBREMIDI_SYMBOL_INIT(snd_midi, event_free)
      LIBREMIDI_SYMBOL_INIT(snd_midi, event_init)
      LIBREMIDI_SYMBOL_INIT(snd_midi, event_new)
      LIBREMIDI_SYMBOL_INIT(snd_midi, event_no_status)
      LIBREMIDI_SYMBOL_INIT(snd_midi, event_resize_buffer)
    }

    bool available{true};
    LIBREMIDI_SYMBOL_DEF(snd_midi, event_decode)
    LIBREMIDI_SYMBOL_DEF(snd_midi, event_encode)
    LIBREMIDI_SYMBOL_DEF(snd_midi, event_free)
    LIBREMIDI_SYMBOL_DEF(snd_midi, event_init)
    LIBREMIDI_SYMBOL_DEF(snd_midi, event_new)
    LIBREMIDI_SYMBOL_DEF(snd_midi, event_no_status)
    LIBREMIDI_SYMBOL_DEF(snd_midi, event_resize_buffer)
  } midi{library};

#if LIBREMIDI_ALSA_HAS_RAMWIDI
  struct rawmidi_t
  {
    explicit rawmidi_t(const dylib_loader& library)
    {
      if (!library)
      {
        available = false;
        return;
      }
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, close)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, info_get_name)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, info_get_subdevice_name)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, info_get_subdevices_count)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, info_set_device)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, info_set_stream)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, info_set_subdevice)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, info_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, open)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, params)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, params_current)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, params_get_buffer_size)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, params_set_clock_type)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, params_set_no_active_sensing)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, params_set_read_mode)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, params_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, poll_descriptors)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, poll_descriptors_count)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, poll_descriptors_revents)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, read)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, status)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, status_get_avail)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, status_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, tread)
      LIBREMIDI_SYMBOL_INIT(snd_rawmidi, write)
    }

    bool available{true};
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, close)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, info_get_name)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, info_get_subdevice_name)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, info_get_subdevices_count)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, info_set_device)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, info_set_stream)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, info_set_subdevice)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, info_sizeof)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, open)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, params)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, params_current)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, params_get_buffer_size)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, params_set_clock_type)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, params_set_no_active_sensing)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, params_set_read_mode)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, params_sizeof)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, poll_descriptors)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, poll_descriptors_count)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, poll_descriptors_revents)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, read)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, status)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, status_get_avail)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, status_sizeof)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, tread)
    LIBREMIDI_SYMBOL_DEF(snd_rawmidi, write)
  } rawmidi{library};
#endif

  struct seq_t
  {
    explicit seq_t(const dylib_loader& library)
#if LIBREMIDI_ALSA_HAS_UMP
        : ump{library}
#endif
    {
      if (!library)
      {
        available = false;
        return;
      }

      LIBREMIDI_SYMBOL_INIT(snd_seq, alloc_queue)
      LIBREMIDI_SYMBOL_INIT(snd_seq, client_id)
      LIBREMIDI_SYMBOL_INIT(snd_seq, client_info_get_client)
      LIBREMIDI_SYMBOL_INIT(snd_seq, client_info_get_name)
      LIBREMIDI_SYMBOL_INIT(snd_seq, client_info_get_card)
      LIBREMIDI_SYMBOL_INIT(snd_seq, client_info_set_client)
      LIBREMIDI_SYMBOL_INIT(snd_seq, client_info_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_seq, close)
      LIBREMIDI_SYMBOL_INIT(snd_seq, connect_from)
      LIBREMIDI_SYMBOL_INIT(snd_seq, control_queue)
      LIBREMIDI_SYMBOL_INIT(snd_seq, create_port)
      LIBREMIDI_SYMBOL_INIT(snd_seq, delete_port)
      LIBREMIDI_SYMBOL_INIT(snd_seq, drain_output)
      LIBREMIDI_SYMBOL_INIT(snd_seq, event_input)
      LIBREMIDI_SYMBOL_INIT(snd_seq, event_input_pending)
      LIBREMIDI_SYMBOL_INIT(snd_seq, event_output)
      LIBREMIDI_SYMBOL_INIT(snd_seq, free_event)
      LIBREMIDI_SYMBOL_INIT(snd_seq, free_queue)
      LIBREMIDI_SYMBOL_INIT(snd_seq, get_any_client_info)
      LIBREMIDI_SYMBOL_INIT(snd_seq, get_any_port_info)
      LIBREMIDI_SYMBOL_INIT(snd_seq, get_port_info)
      LIBREMIDI_SYMBOL_INIT(snd_seq, open)
      LIBREMIDI_SYMBOL_INIT(snd_seq, poll_descriptors)
      LIBREMIDI_SYMBOL_INIT(snd_seq, poll_descriptors_count)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_get_addr)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_get_capability)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_get_name)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_get_port)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_get_type)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_capability)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_client)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_midi_channels)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_name)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_port)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_timestamping)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_timestamp_queue)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_timestamp_real)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_set_type)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_info_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_free)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_malloc)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_dest)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_sender)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_time_real)
      LIBREMIDI_SYMBOL_INIT(snd_seq, port_subscribe_set_time_update)
      LIBREMIDI_SYMBOL_INIT(snd_seq, query_next_client)
      LIBREMIDI_SYMBOL_INIT(snd_seq, query_next_port)
      LIBREMIDI_SYMBOL_INIT(snd_seq, queue_tempo_set_ppq)
      LIBREMIDI_SYMBOL_INIT(snd_seq, queue_tempo_set_tempo)
      LIBREMIDI_SYMBOL_INIT(snd_seq, queue_tempo_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_seq, set_client_name)
      LIBREMIDI_SYMBOL_INIT(snd_seq, set_port_info)
      LIBREMIDI_SYMBOL_INIT(snd_seq, set_queue_tempo)
      LIBREMIDI_SYMBOL_INIT(snd_seq, subscribe_port)
      LIBREMIDI_SYMBOL_INIT(snd_seq, unsubscribe_port)
    }

    bool available{true};
    LIBREMIDI_SYMBOL_DEF(snd_seq, alloc_queue)
    LIBREMIDI_SYMBOL_DEF(snd_seq, client_id)
    LIBREMIDI_SYMBOL_DEF(snd_seq, client_info_get_client)
    LIBREMIDI_SYMBOL_DEF(snd_seq, client_info_get_name)
    LIBREMIDI_SYMBOL_DEF(snd_seq, client_info_get_card)
    LIBREMIDI_SYMBOL_DEF(snd_seq, client_info_set_client)
    LIBREMIDI_SYMBOL_DEF(snd_seq, client_info_sizeof)
    LIBREMIDI_SYMBOL_DEF(snd_seq, close)
    LIBREMIDI_SYMBOL_DEF(snd_seq, connect_from)
    LIBREMIDI_SYMBOL_DEF(snd_seq, control_queue)
    LIBREMIDI_SYMBOL_DEF(snd_seq, create_port)
    LIBREMIDI_SYMBOL_DEF(snd_seq, delete_port)
    LIBREMIDI_SYMBOL_DEF(snd_seq, drain_output)
    LIBREMIDI_SYMBOL_DEF(snd_seq, event_input)
    LIBREMIDI_SYMBOL_DEF(snd_seq, event_input_pending)
    LIBREMIDI_SYMBOL_DEF(snd_seq, event_output)
    LIBREMIDI_SYMBOL_DEF(snd_seq, free_event)
    LIBREMIDI_SYMBOL_DEF(snd_seq, free_queue)
    LIBREMIDI_SYMBOL_DEF(snd_seq, get_any_client_info)
    LIBREMIDI_SYMBOL_DEF(snd_seq, get_any_port_info)
    LIBREMIDI_SYMBOL_DEF(snd_seq, get_port_info)
    LIBREMIDI_SYMBOL_DEF(snd_seq, open)
    LIBREMIDI_SYMBOL_DEF(snd_seq, poll_descriptors)
    LIBREMIDI_SYMBOL_DEF(snd_seq, poll_descriptors_count)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_get_addr)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_get_capability)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_get_name)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_get_port)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_get_type)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_capability)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_client)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_midi_channels)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_name)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_port)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_timestamping)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_timestamp_queue)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_timestamp_real)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_set_type)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_info_sizeof)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_free)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_malloc)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_dest)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_sender)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_time_real)
    LIBREMIDI_SYMBOL_DEF(snd_seq, port_subscribe_set_time_update)
    LIBREMIDI_SYMBOL_DEF(snd_seq, query_next_client)
    LIBREMIDI_SYMBOL_DEF(snd_seq, query_next_port)
    LIBREMIDI_SYMBOL_DEF(snd_seq, queue_tempo_set_ppq)
    LIBREMIDI_SYMBOL_DEF(snd_seq, queue_tempo_set_tempo)
    LIBREMIDI_SYMBOL_DEF(snd_seq, queue_tempo_sizeof)

    LIBREMIDI_SYMBOL_DEF(snd_seq, set_client_name)
    LIBREMIDI_SYMBOL_DEF(snd_seq, set_port_info)
    LIBREMIDI_SYMBOL_DEF(snd_seq, set_queue_tempo)
    LIBREMIDI_SYMBOL_DEF(snd_seq, subscribe_port)
    LIBREMIDI_SYMBOL_DEF(snd_seq, unsubscribe_port)

#if LIBREMIDI_ALSA_HAS_UMP
    struct ump_t
    {
      explicit ump_t(const dylib_loader& library)
      {
        if (!library)
        {
          available = false;
          return;
        }
        LIBREMIDI_SYMBOL_INIT(snd_seq, set_client_midi_version)
        LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_input)
        LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output)
        LIBREMIDI_SYMBOL_INIT(snd_seq_ump, event_output_direct)
      }

      bool available{true};

      LIBREMIDI_SYMBOL_DEF(snd_seq, set_client_midi_version)
      LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_input)
      LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output)
      LIBREMIDI_SYMBOL_DEF(snd_seq_ump, event_output_direct)
    } ump;
#endif
  } seq{library};

#if LIBREMIDI_ALSA_HAS_UMP
  struct ump_t
  {
    explicit ump_t(const dylib_loader& library)
    {
      if (!library)
      {
        available = false;
        return;
      }

      LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_get_name)
      LIBREMIDI_SYMBOL_INIT(snd_ump, block_info_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_ump, close)
      LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_get_name)
      LIBREMIDI_SYMBOL_INIT(snd_ump, endpoint_info_sizeof)
      LIBREMIDI_SYMBOL_INIT(snd_ump, open)
      LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors)
      LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors_count)
      LIBREMIDI_SYMBOL_INIT(snd_ump, poll_descriptors_revents)
      LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi)
      LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params)
      LIBREMIDI_SYMBOL_INIT(snd_ump, rawmidi_params_current)
      LIBREMIDI_SYMBOL_INIT(snd_ump, read)
      LIBREMIDI_SYMBOL_INIT(snd_ump, tread)
      LIBREMIDI_SYMBOL_INIT(snd_ump, write)
    }

    bool available{true};
    LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_get_name)
    LIBREMIDI_SYMBOL_DEF(snd_ump, block_info_sizeof)
    LIBREMIDI_SYMBOL_DEF(snd_ump, close)
    LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_get_name)
    LIBREMIDI_SYMBOL_DEF(snd_ump, endpoint_info_sizeof)
    LIBREMIDI_SYMBOL_DEF(snd_ump, open)
    LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors)
    LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors_count)
    LIBREMIDI_SYMBOL_DEF(snd_ump, poll_descriptors_revents)
    LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi)
    LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params)
    LIBREMIDI_SYMBOL_DEF(snd_ump, rawmidi_params_current)
    LIBREMIDI_SYMBOL_DEF(snd_ump, read)
    LIBREMIDI_SYMBOL_DEF(snd_ump, tread)
    LIBREMIDI_SYMBOL_DEF(snd_ump, write)
  } ump{library};
#endif
};

#undef snd_dylib_alloca
#define snd_dylib_alloca(ptr, access, type)                                \
  {                                                                        \
    *ptr = (snd_##access##_##type##_t*)alloca(snd.access.type##_sizeof()); \
    memset(*ptr, 0, snd.access.type##_sizeof());                           \
  }
#define snd_dylib_alloca2(ptr, access1, access2, type)                                         \
  {                                                                                            \
    *ptr = (snd_##access1##_access2##_##type##_t*)alloca(snd.access1.access2.type##_sizeof()); \
    memset(*ptr, 0, snd.access1.access2.type##_sizeof());                                      \
  }

#undef snd_rawmidi_info_alloca
#define snd_rawmidi_info_alloca(ptr) snd_dylib_alloca(ptr, rawmidi, info)
#undef snd_rawmidi_params_alloca
#define snd_rawmidi_params_alloca(ptr) snd_dylib_alloca(ptr, rawmidi, params)
#undef snd_rawmidi_status_alloca
#define snd_rawmidi_status_alloca(ptr) snd_dylib_alloca(ptr, rawmidi, status)

#undef snd_seq_client_info_alloca
#define snd_seq_client_info_alloca(ptr) snd_dylib_alloca(ptr, seq, client_info)
#undef snd_seq_port_info_alloca
#define snd_seq_port_info_alloca(ptr) snd_dylib_alloca(ptr, seq, port_info)
#undef snd_seq_queue_tempo_alloca
#define snd_seq_queue_tempo_alloca(ptr) snd_dylib_alloca(ptr, seq, queue_tempo)

#if LIBREMIDI_ALSA_HAS_UMP
  #undef snd_ump_block_info_alloca
  #define snd_ump_block_info_alloca(ptr) snd_dylib_alloca(ptr, ump, block_info)
  #undef snd_ump_endpoint_info_alloca
  #define snd_ump_endpoint_info_alloca(ptr) snd_dylib_alloca(ptr, ump, endpoint_info)
#endif
}
