diff --git a/libremidi.cabal b/libremidi.cabal
--- a/libremidi.cabal
+++ b/libremidi.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           libremidi
-version:        0.4.1
+version:        0.4.2
 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
diff --git a/src/Libremidi/Api.hs b/src/Libremidi/Api.hs
--- a/src/Libremidi/Api.hs
+++ b/src/Libremidi/Api.hs
@@ -3,6 +3,7 @@
 -- Operations in ErrM can possibly fail.
 module Libremidi.Api where
 
+import Control.Monad ((>=>))
 import Control.Monad.IO.Class (liftIO)
 import Data.Coerce (coerce)
 import Data.Default (Default (..))
@@ -31,6 +32,7 @@
   , fromCLong
   , pokeField
   , takeM
+  , text0M
   , textM
   , toCBool
   , toCLong
@@ -76,10 +78,14 @@
   | ApiWindowsUwp
   | ApiWebmidi
   | ApiPipewire
+  | ApiKeyboard
+  | ApiNetwork
   | ApiAlsaRawUmp
   | ApiAlsaSeqUmp
   | ApiCoremidiUmp
   | ApiWindowsMidiServices
+  | ApiKeyboardUmp
+  | ApiNetworkUmp
   | ApiDummy
   deriving stock (Eq, Ord, Show, Enum, Bounded)
 
@@ -94,10 +100,14 @@
     a | a == LMF.apiWindowsUwp -> Just ApiWindowsUwp
     a | a == LMF.apiWebmidi -> Just ApiWebmidi
     a | a == LMF.apiPipewire -> Just ApiPipewire
+    a | a == LMF.apiKeyboard -> Just ApiKeyboard
+    a | a == LMF.apiNetwork -> Just ApiNetwork
     a | a == LMF.apiAlsaRawUmp -> Just ApiAlsaRawUmp
     a | a == LMF.apiAlsaSeqUmp -> Just ApiAlsaSeqUmp
     a | a == LMF.apiCoremidiUmp -> Just ApiCoremidiUmp
     a | a == LMF.apiWindowsMidiServices -> Just ApiWindowsMidiServices
+    a | a == LMF.apiKeyboardUmp -> Just ApiKeyboardUmp
+    a | a == LMF.apiNetworkUmp -> Just ApiNetworkUmp
     a | a == LMF.apiDummy -> Just ApiDummy
     _ -> Nothing
   toBitEnum = \case
@@ -110,10 +120,14 @@
     ApiWindowsUwp -> LMF.apiWindowsUwp
     ApiWebmidi -> LMF.apiWebmidi
     ApiPipewire -> LMF.apiPipewire
+    ApiKeyboard -> LMF.apiKeyboard
+    ApiNetwork -> LMF.apiNetwork
     ApiAlsaRawUmp -> LMF.apiAlsaRawUmp
     ApiAlsaSeqUmp -> LMF.apiAlsaSeqUmp
     ApiCoremidiUmp -> LMF.apiCoremidiUmp
     ApiWindowsMidiServices -> LMF.apiWindowsMidiServices
+    ApiKeyboardUmp -> LMF.apiKeyboardUmp
+    ApiNetworkUmp -> LMF.apiNetworkUmp
     ApiDummy -> LMF.apiDummy
 
 data ConfigType
@@ -507,3 +521,31 @@
 outSchedMsg2 h ts msg len = unRunErrM $
   withForeignPtr h $ \p ->
     checkM (LMF.libremidi_midi_out_schedule_ump p (toCLong ts) msg (toCSize (fromIntegral len)))
+
+getVersion :: IO Text
+getVersion = LMF.libremidi_get_version >>= text0M
+
+type AvailFun = Maybe Api -> IO ()
+
+newAvailCb :: AvailFun -> IO (Cb LMF.AvailFun)
+newAvailCb f = LMF.newAvailCb (\_ -> f . fromBitEnum)
+
+availApis1 :: AvailFun -> IO ()
+availApis1 f = do
+  cb <- newAvailCb f
+  withCb cb (LMF.libremidi_midi1_available_apis nullPtr)
+
+availApis2 :: AvailFun -> IO ()
+availApis2 f = do
+  cb <- newAvailCb f
+  withCb cb (LMF.libremidi_midi2_available_apis nullPtr)
+
+apiIdentifier :: Api -> IO Text
+apiIdentifier = LMF.libremidi_api_identifier . toBitEnum >=> text0M
+
+apiDisplayName :: Api -> IO Text
+apiDisplayName = LMF.libremidi_api_display_name . toBitEnum >=> text0M
+
+getCompiledApiByIdentifier :: Text -> IO (Maybe Api)
+getCompiledApiByIdentifier n =
+  TF.withCString n (fmap fromBitEnum . LMF.libremidi_get_compiled_api_by_identifier)
diff --git a/src/Libremidi/Common.hs b/src/Libremidi/Common.hs
--- a/src/Libremidi/Common.hs
+++ b/src/Libremidi/Common.hs
@@ -5,7 +5,6 @@
 import Control.Exception (Exception, finally, mask, mask_, throwIO)
 import Control.Monad.Except (ExceptT (..), MonadError (..), runExceptT)
 import Control.Monad.IO.Class (MonadIO (..))
-import Data.Coerce (coerce)
 import Data.Foldable (traverse_)
 import Data.Int (Int32, Int64)
 import Data.Kind (Type)
@@ -190,6 +189,15 @@
     then fmap Right two
     else pure (Left e)
 
+-- TODO use TF.peekCString when available in text 2.1.2
+foreign import ccall "string.h strlen"
+  strlen :: CString -> IO CSize
+
+text0M :: CString -> IO Text
+text0M s = do
+  l <- strlen s
+  TF.peekCStringLen (s, fromIntegral l)
+
 textM :: (Ptr CString -> Ptr CSize -> IO Err) -> ErrM Text
 textM f = unRunErrM $
   allocaPtr $ \sptr -> do
@@ -197,7 +205,7 @@
       checkAndThenM (f sptr lptr) $ do
         s <- peek sptr
         l <- peek lptr
-        TF.fromPtr (coerce s) (fromIntegral l)
+        TF.peekCStringLen (s, fromIntegral l)
 
 takeM :: (Ptr (Ptr x) -> IO Err) -> ErrM (ForeignPtr x)
 takeM f = unRunErrM $
diff --git a/src/Libremidi/Foreign.hsc b/src/Libremidi/Foreign.hsc
--- a/src/Libremidi/Foreign.hsc
+++ b/src/Libremidi/Foreign.hsc
@@ -10,6 +10,7 @@
 import Foreign.Ptr (Ptr, FunPtr)
 import Libremidi.Common (Err (..), Field, Cb, mallocForeignPtrBytes0, newCb, ptrSize, MallocPtr (..), newField)
 
+#include "libremidi/api-c.h"
 #include "libremidi/libremidi-c.h"
 
 #{enum TimestampMode
@@ -35,10 +36,14 @@
  , apiWindowsUwp = WINDOWS_UWP
  , apiWebmidi = WEBMIDI
  , apiPipewire = PIPEWIRE
+ , apiKeyboard = KEYBOARD
+ , apiNetwork = NETWORK
  , apiAlsaRawUmp = ALSA_RAW_UMP
  , apiAlsaSeqUmp = ALSA_SEQ_UMP
  , apiCoremidiUmp = COREMIDI_UMP
  , apiWindowsMidiServices = WINDOWS_MIDI_SERVICES
+ , apiKeyboardUmp = KEYBOARD_UMP
+ , apiNetworkUmp = NETWORK_UMP
  , apiDummy = DUMMY
 }
 
@@ -75,6 +80,7 @@
 
 data ApiConfig
 
+-- TODO Use libremidi_midi_api_configuration_init
 instance MallocPtr ApiConfig where
   mallocPtr _ = mallocForeignPtrBytes0 #{size libremidi_api_configuration}
 
@@ -103,8 +109,17 @@
 newObsCb :: ObsFun p -> IO (Cb (ObsFun p))
 newObsCb = newCb obsWrap
 
+type AvailFun = Ptr Void -> Api -> IO ()
+
+foreign import ccall "wrapper"
+  availWrap :: AvailFun -> IO (FunPtr AvailFun)
+
+newAvailCb :: AvailFun -> IO (Cb AvailFun)
+newAvailCb = newCb availWrap
+
 data ObsConfig
 
+-- TODO Use libremidi_midi_observer_configuration_init
 instance MallocPtr ObsConfig where
   mallocPtr _ = mallocForeignPtrBytes0 #{size libremidi_observer_configuration}
 
@@ -156,6 +171,7 @@
 
 data MidiConfig
 
+-- TODO Use libremidi_midi_configuration_init
 instance MallocPtr MidiConfig where
   mallocPtr _ = mallocForeignPtrBytes0 #{size libremidi_midi_configuration}
 
@@ -200,6 +216,24 @@
 
 mcTimestamps :: Field MidiConfig TimestampMode
 mcTimestamps = newField #{offset libremidi_midi_configuration, timestamps}
+
+foreign import ccall "libremidi/libremidi-c.h"
+  libremidi_get_version :: IO CString
+
+foreign import ccall "libremidi/libremidi-c.h"
+  libremidi_midi1_available_apis :: Ptr Void -> FunPtr AvailFun -> IO ()
+
+foreign import ccall "libremidi/libremidi-c.h"
+  libremidi_midi2_available_apis :: Ptr Void -> FunPtr AvailFun -> IO ()
+
+foreign import ccall "libremidi/libremidi-c.h"
+  libremidi_api_identifier :: Api -> IO CString
+
+foreign import ccall "libremidi/libremidi-c.h"
+  libremidi_api_display_name :: Api -> IO CString
+
+foreign import ccall "libremidi/libremidi-c.h"
+  libremidi_get_compiled_api_by_identifier :: CString -> IO Api
 
 foreign import ccall "libremidi/libremidi-c.h"
   libremidi_midi_in_port_clone :: Ptr InPort -> Ptr (Ptr InPort) -> IO Err
diff --git a/src/Libremidi/Simple.hs b/src/Libremidi/Simple.hs
--- a/src/Libremidi/Simple.hs
+++ b/src/Libremidi/Simple.hs
@@ -111,3 +111,18 @@
     let mc = def {LMA.mcPort = Just (LMA.MidiPortOut op')}
     mc' <- liftIO (LMA.setMidiLogCb lf mc)
     LMA.newOutHandle LMA.ApiUnspecified mc'
+
+listAvailFun :: IORef (Seq LMA.Api) -> LMA.AvailFun
+listAvailFun r = maybe (pure ()) (modifyIORef' r . flip (:|>))
+
+listAvailApis1 :: IO (Seq LMA.Api)
+listAvailApis1 = do
+  r <- newIORef Empty
+  LMA.availApis1 (listAvailFun r)
+  readIORef r
+
+listAvailApis2 :: IO (Seq LMA.Api)
+listAvailApis2 = do
+  r <- newIORef Empty
+  LMA.availApis2 (listAvailFun r)
+  readIORef r
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,21 +1,34 @@
+{-# LANGUAGE OverloadedStrings #-}
+
 module Main
   ( main
   )
 where
 
+import Libremidi.Api qualified as LMA
+import Libremidi.Simple qualified as LMS
 import Test.Tasty (TestTree, defaultMain, testGroup)
-import Test.Tasty.HUnit (testCase, (@?=))
+import Test.Tasty.HUnit (assertBool, testCase, (@?=))
 
-testDummy :: TestTree
-testDummy = testCase "dummy" $ do
-  let actual = (1 + 1) :: Int
-      expected = 2 :: Int
-  actual @?= expected
+testMetadata :: TestTree
+testMetadata = testCase "metadata" $ do
+  ver <- LMA.getVersion
+  ver @?= "4.5.0"
+  avail1 <- LMS.listAvailApis1
+  assertBool "midi1 supports dummy" (LMA.ApiDummy `elem` avail1)
+  avail2 <- LMS.listAvailApis2
+  assertBool "midi2 supports dummy" (LMA.ApiDummy `elem` avail2)
+  dummyIdent <- LMA.apiIdentifier LMA.ApiDummy
+  dummyIdent @?= "dummy"
+  dummyName <- LMA.apiDisplayName LMA.ApiDummy
+  dummyName @?= "Dummy"
+  dummyApi <- LMA.getCompiledApiByIdentifier "dummy"
+  dummyApi @?= Just LMA.ApiDummy
 
 main :: IO ()
 main =
   defaultMain $
     testGroup
       "Libremidi"
-      [ testDummy
+      [ testMetadata
       ]
diff --git a/vendor/libremidi/api.hpp b/vendor/libremidi/api.hpp
--- a/vendor/libremidi/api.hpp
+++ b/vendor/libremidi/api.hpp
@@ -2,7 +2,6 @@
 #include <libremidi/api-c.h>
 #include <libremidi/config.hpp>
 
-#include <any>
 #include <string_view>
 #include <vector>
 
@@ -30,9 +29,6 @@
   API compiled for certain operating systems.
 */
 LIBREMIDI_EXPORT std::vector<libremidi::API> available_ump_apis() noexcept;
-
-LIBREMIDI_EXPORT
-libremidi::API midi_api(const std::any& conf);
 
 //! A static function to determine the current version.
 LIBREMIDI_EXPORT std::string_view get_version() noexcept;
diff --git a/vendor/libremidi/backends.hpp b/vendor/libremidi/backends.hpp
--- a/vendor/libremidi/backends.hpp
+++ b/vendor/libremidi/backends.hpp
@@ -1,7 +1,6 @@
 #pragma once
 #include <libremidi/config.hpp>
 
-#include <any>
 #include <tuple>
 
 #if !__has_include(<weak_libjack.h>) && !__has_include(<jack/jack.h>)
@@ -202,10 +201,10 @@
   midi2::for_backend(api, f);
 }
 
-void for_input_configuration(auto f, std::any& api_conf)
+void for_input_configuration(auto f, libremidi::input_api_configuration& api_conf)
 {
   auto from_api = [&]<typename T>(T& /*backend*/) mutable {
-    if (auto conf = std::any_cast<typename T::midi_in_configuration>(&api_conf))
+    if (auto conf = std::get_if<typename T::midi_in_configuration>(&api_conf))
     {
       f(*conf);
       return true;
@@ -217,10 +216,10 @@
   std::apply([&](auto&&... b) { return (from_api(b) || ...); }, midi2::available_backends);
 }
 
-void for_output_configuration(auto f, std::any& api_conf)
+void for_output_configuration(auto f, libremidi::output_api_configuration& api_conf)
 {
   auto from_api = [&]<typename T>(T& /*backend*/) mutable {
-    if (auto conf = std::any_cast<typename T::midi_out_configuration>(&api_conf))
+    if (auto conf = std::get_if<typename T::midi_out_configuration>(&api_conf))
     {
       f(*conf);
       return true;
@@ -232,10 +231,10 @@
   std::apply([&](auto&&... b) { return (from_api(b) || ...); }, midi2::available_backends);
 }
 
-void for_observer_configuration(auto f, std::any& api_conf)
+void for_observer_configuration(auto f, libremidi::observer_api_configuration& api_conf)
 {
   auto from_api = [&]<typename T>(T& /*backend*/) mutable {
-    if (auto conf = std::any_cast<typename T::midi_observer_configuration>(&api_conf))
+    if (auto conf = std::get_if<typename T::midi_observer_configuration>(&api_conf))
     {
       f(*conf);
       return true;
diff --git a/vendor/libremidi/backends/alsa_raw.hpp b/vendor/libremidi/backends/alsa_raw.hpp
--- a/vendor/libremidi/backends/alsa_raw.hpp
+++ b/vendor/libremidi/backends/alsa_raw.hpp
@@ -3,6 +3,7 @@
 #include <libremidi/backends/alsa_raw/midi_out.hpp>
 #include <libremidi/backends/alsa_raw/observer.hpp>
 
+#include <string_view>
 // Credits: greatly inspired from
 // https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawmidiout.c
 // https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawportlist.c
@@ -19,8 +20,8 @@
   using midi_out_configuration = alsa_raw_output_configuration;
   using midi_observer_configuration = alsa_raw_observer_configuration;
   static const constexpr auto API = libremidi::API::ALSA_RAW;
-  static const constexpr auto name = "alsa_raw";
-  static const constexpr auto display_name = "ALSA (raw)";
+  static const constexpr std::string_view name = "alsa_raw";
+  static const constexpr std::string_view display_name = "ALSA (raw)";
 
   static inline bool available() noexcept
   {
diff --git a/vendor/libremidi/backends/alsa_raw_ump.hpp b/vendor/libremidi/backends/alsa_raw_ump.hpp
--- a/vendor/libremidi/backends/alsa_raw_ump.hpp
+++ b/vendor/libremidi/backends/alsa_raw_ump.hpp
@@ -5,6 +5,8 @@
 #include <libremidi/backends/alsa_raw_ump/observer.hpp>
 #include <libremidi/backends/dummy.hpp>
 
+#include <string_view>
+
 namespace libremidi::alsa_raw_ump
 {
 struct backend
@@ -14,12 +16,11 @@
   using midi_observer = observer_impl;
   using midi_in_configuration = alsa_raw_ump::input_configuration;
   using midi_out_configuration = alsa_raw_ump::output_configuration;
-  struct midi_observer_configuration : alsa_raw_observer_configuration
-  {
-  };
+  using midi_observer_configuration = alsa_raw_ump::observer_configuration;
+
   static const constexpr auto API = libremidi::API::ALSA_RAW_UMP;
-  static const constexpr auto name = "alsa_raw_ump";
-  static const constexpr auto display_name = "ALSA (raw UMP)";
+  static const constexpr std::string_view name = "alsa_raw_ump";
+  static const constexpr std::string_view display_name = "ALSA (raw UMP)";
 
   static inline bool available() noexcept
   {
diff --git a/vendor/libremidi/backends/alsa_raw_ump/config.hpp b/vendor/libremidi/backends/alsa_raw_ump/config.hpp
--- a/vendor/libremidi/backends/alsa_raw_ump/config.hpp
+++ b/vendor/libremidi/backends/alsa_raw_ump/config.hpp
@@ -18,8 +18,7 @@
   std::optional<chunking_parameters> chunking;
 };
 
-struct observer_configuration
+struct observer_configuration : public alsa_raw_observer_configuration
 {
-  std::chrono::milliseconds poll_period{100};
 };
 }
diff --git a/vendor/libremidi/backends/alsa_seq.hpp b/vendor/libremidi/backends/alsa_seq.hpp
--- a/vendor/libremidi/backends/alsa_seq.hpp
+++ b/vendor/libremidi/backends/alsa_seq.hpp
@@ -16,6 +16,8 @@
 #include <libremidi/backends/alsa_seq/midi_out.hpp>
 #include <libremidi/backends/alsa_seq/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi::alsa_seq
 {
 
@@ -29,8 +31,8 @@
   using midi_out_configuration = alsa_seq::output_configuration;
   using midi_observer_configuration = alsa_seq::observer_configuration;
   static const constexpr auto API = libremidi::API::ALSA_SEQ;
-  static const constexpr auto name = "alsa_seq";
-  static const constexpr auto display_name = "ALSA (sequencer)";
+  static const constexpr std::string_view name = "alsa_seq";
+  static const constexpr std::string_view display_name = "ALSA (sequencer)";
 
   static inline bool available() noexcept
   {
diff --git a/vendor/libremidi/backends/alsa_seq/observer.hpp b/vendor/libremidi/backends/alsa_seq/observer.hpp
--- a/vendor/libremidi/backends/alsa_seq/observer.hpp
+++ b/vendor/libremidi/backends/alsa_seq/observer.hpp
@@ -107,8 +107,8 @@
     const auto tp = snd.seq.port_info_get_type(pinfo);
     bool ok = this->configuration.track_any;
 
-    static constexpr auto virtual_port
-        = SND_SEQ_PORT_TYPE_SOFTWARE | SND_SEQ_PORT_TYPE_SYNTHESIZER;
+    static constexpr auto virtual_port = SND_SEQ_PORT_TYPE_SOFTWARE | SND_SEQ_PORT_TYPE_SYNTHESIZER
+                                         | SND_SEQ_PORT_TYPE_APPLICATION;
 
     if ((tp & SND_SEQ_PORT_TYPE_HARDWARE) && this->configuration.track_hardware)
       ok = true;
diff --git a/vendor/libremidi/backends/alsa_seq_ump.hpp b/vendor/libremidi/backends/alsa_seq_ump.hpp
--- a/vendor/libremidi/backends/alsa_seq_ump.hpp
+++ b/vendor/libremidi/backends/alsa_seq_ump.hpp
@@ -5,6 +5,8 @@
 #include <libremidi/backends/alsa_seq_ump/config.hpp>
 #include <libremidi/backends/alsa_seq_ump/midi_out.hpp>
 
+#include <string_view>
+
 namespace libremidi
 {
 template <>
@@ -50,8 +52,8 @@
   using midi_out_configuration = alsa_seq_ump::output_configuration;
   using midi_observer_configuration = alsa_seq_ump::observer_configuration;
   static const constexpr auto API = libremidi::API::ALSA_SEQ_UMP;
-  static const constexpr auto name = "alsa_seq_ump";
-  static const constexpr auto display_name = "ALSA (sequencer, UMP)";
+  static const constexpr std::string_view name = "alsa_seq_ump";
+  static const constexpr std::string_view display_name = "ALSA (sequencer, UMP)";
 
   static inline bool available() noexcept
   {
diff --git a/vendor/libremidi/backends/coremidi.hpp b/vendor/libremidi/backends/coremidi.hpp
--- a/vendor/libremidi/backends/coremidi.hpp
+++ b/vendor/libremidi/backends/coremidi.hpp
@@ -3,6 +3,8 @@
 #include <libremidi/backends/coremidi/midi_out.hpp>
 #include <libremidi/backends/coremidi/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi
 {
 struct core_backend
@@ -14,8 +16,8 @@
   using midi_out_configuration = coremidi_output_configuration;
   using midi_observer_configuration = coremidi_observer_configuration;
   static const constexpr auto API = libremidi::API::COREMIDI;
-  static const constexpr auto name = "core";
-  static const constexpr auto display_name = "CoreMIDI";
+  static const constexpr std::string_view name = "core";
+  static const constexpr std::string_view display_name = "CoreMIDI";
 
   static constexpr inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/coremidi_ump.hpp b/vendor/libremidi/backends/coremidi_ump.hpp
--- a/vendor/libremidi/backends/coremidi_ump.hpp
+++ b/vendor/libremidi/backends/coremidi_ump.hpp
@@ -3,6 +3,8 @@
 #include <libremidi/backends/coremidi_ump/midi_out.hpp>
 #include <libremidi/backends/coremidi_ump/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi::coremidi_ump
 {
 struct backend
@@ -14,8 +16,8 @@
   using midi_out_configuration = coremidi_ump::output_configuration;
   using midi_observer_configuration = coremidi_ump::observer_configuration;
   static const constexpr auto API = libremidi::API::COREMIDI_UMP;
-  static const constexpr auto name = "core_ump";
-  static const constexpr auto display_name = "CoreMIDI UMP";
+  static const constexpr std::string_view name = "core_ump";
+  static const constexpr std::string_view display_name = "CoreMIDI UMP";
 
   static constexpr inline bool available() noexcept { return true; /* todo? */ }
 };
diff --git a/vendor/libremidi/backends/dummy.hpp b/vendor/libremidi/backends/dummy.hpp
--- a/vendor/libremidi/backends/dummy.hpp
+++ b/vendor/libremidi/backends/dummy.hpp
@@ -5,6 +5,8 @@
 #include <libremidi/detail/midi_out.hpp>
 #include <libremidi/detail/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi
 {
 class observer_dummy : public observer_api
@@ -23,11 +25,11 @@
     , public error_handler
 {
 public:
-  explicit midi_in_dummy(const input_configuration& configuration, std::any)
+  explicit midi_in_dummy(const input_configuration& configuration, input_api_configuration)
   {
     libremidi_handle_warning(configuration, "This class provides no functionality.");
   }
-  explicit midi_in_dummy(const ump_input_configuration& configuration, std::any)
+  explicit midi_in_dummy(const ump_input_configuration& configuration, input_api_configuration)
   {
     libremidi_handle_warning(configuration, "This class provides no functionality.");
   }
@@ -58,7 +60,7 @@
     , public error_handler
 {
 public:
-  explicit midi_out_dummy(const output_configuration& configuration, std::any)
+  explicit midi_out_dummy(const output_configuration& configuration, output_api_configuration)
   {
     libremidi_handle_warning(configuration, "This class provides no functionality.");
   }
@@ -97,8 +99,8 @@
   using midi_out_configuration = dummy_configuration;
   using midi_observer_configuration = dummy_configuration;
   static const constexpr auto API = libremidi::API::DUMMY;
-  static const constexpr auto name = "dummy";
-  static const constexpr auto display_name = "Dummy";
+  static const constexpr std::string_view name = "dummy";
+  static const constexpr std::string_view display_name = "Dummy";
 
   static constexpr inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/emscripten.hpp b/vendor/libremidi/backends/emscripten.hpp
--- a/vendor/libremidi/backends/emscripten.hpp
+++ b/vendor/libremidi/backends/emscripten.hpp
@@ -7,6 +7,8 @@
   #include <libremidi/backends/emscripten/midi_out.hpp>
   #include <libremidi/backends/emscripten/observer.hpp>
 
+  #include <string_view>
+
 namespace libremidi
 {
 
@@ -19,8 +21,8 @@
   using midi_out_configuration = emscripten_output_configuration;
   using midi_observer_configuration = emscripten_observer_configuration;
   static const constexpr auto API = libremidi::API::WEBMIDI;
-  static const constexpr auto name = "webmidi";
-  static const constexpr auto display_name = "WebMIDI";
+  static const constexpr std::string_view name = "webmidi";
+  static const constexpr std::string_view display_name = "WebMIDI";
 
   static constexpr inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/jack.hpp b/vendor/libremidi/backends/jack.hpp
--- a/vendor/libremidi/backends/jack.hpp
+++ b/vendor/libremidi/backends/jack.hpp
@@ -3,6 +3,8 @@
 #include <libremidi/backends/jack/midi_out.hpp>
 #include <libremidi/backends/jack/observer.hpp>
 
+#include <string_view>
+
 //*********************************************************************//
 //  API: UNIX JACK
 //
@@ -22,8 +24,8 @@
   using midi_out_configuration = jack_output_configuration;
   using midi_observer_configuration = jack_observer_configuration;
   static const constexpr auto API = libremidi::API::JACK_MIDI;
-  static const constexpr auto name = "jack";
-  static const constexpr auto display_name = "JACK";
+  static const constexpr std::string_view name = "jack";
+  static const constexpr std::string_view display_name = "JACK";
 
   static inline bool available() noexcept
   {
diff --git a/vendor/libremidi/backends/keyboard.hpp b/vendor/libremidi/backends/keyboard.hpp
--- a/vendor/libremidi/backends/keyboard.hpp
+++ b/vendor/libremidi/backends/keyboard.hpp
@@ -2,6 +2,8 @@
 #include <libremidi/backends/dummy.hpp>
 #include <libremidi/backends/keyboard/midi_in.hpp>
 
+#include <string_view>
+
 namespace libremidi
 {
 struct kbd_backend
@@ -13,8 +15,8 @@
   using midi_out_configuration = dummy_configuration;
   using midi_observer_configuration = dummy_configuration;
   static const constexpr auto API = libremidi::API::KEYBOARD;
-  static const constexpr auto name = "keyboard";
-  static const constexpr auto display_name = "Computer keyboard";
+  static const constexpr std::string_view name = "keyboard";
+  static const constexpr std::string_view display_name = "Computer keyboard";
 
   static inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/keyboard/config.hpp b/vendor/libremidi/backends/keyboard/config.hpp
--- a/vendor/libremidi/backends/keyboard/config.hpp
+++ b/vendor/libremidi/backends/keyboard/config.hpp
@@ -6,6 +6,128 @@
 
 namespace libremidi
 {
+enum kbd_event
+{
+  NOTE_0 = 0x0,         // C
+  VEL_0 = NOTE_0 + 128, // Set velocity to 0
+  OCT_0 = VEL_0 + 128,  // Set octave to 0
+  OCTAVE_PLUS = OCT_0 + 128,
+  OCTAVE_MINUS,
+  VELOCITY_PLUS,
+  VELOCITY_MINUS,
+};
+
+// Default map
+//
+// ,---,---,---,---,---,---,---,---,---,---,---,---,---,-------,
+// | V0| V1| V2| V3| V4| V5| V6| V7| V8| V9|V10|V11|V12| <-    |
+// |---'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-----|
+// | ->| |   | C#| D#|   | F#| G#| A#|   | C#| D#|   | F#|     |
+// |-----',--',--',--',--',--',--',--',--',--',--',--',--'|    |
+// | Caps | C | D | E | F | G | A | B | C | D | E | F | G |    |
+// |----,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'---'----|
+// | -^ |   | O-| O+| V-| V+|   |   |   |   |   |   |   ----^  |
+// |----'-,-',--'--,'---'---'---'---'---'---'-,-'---',--,------|
+// | ctrl |  | alt |                          |altgr |  | ctrl |
+// '------'  '-----'--------------------------'------'  '------'
+
+// clang-format off
+
+// actually based on macOS virtual codes as it seems
+// impossible to get raw scan codes.
+// https://eastmanreference.com/complete-list-of-applescript-key-codes
+static inline const std::map<int, int>& scancode_map_macos(){
+  static const std::map<int, int> ret{
+  {  0, NOTE_0}, // C0
+  { 13, NOTE_0 + 1},
+  {  1, NOTE_0 + 2},
+  { 14, NOTE_0 + 3},
+  {  2, NOTE_0 + 4},
+  {  3, NOTE_0 + 5},
+  { 17, NOTE_0 + 6},
+  {  5, NOTE_0 + 7},
+  { 16, NOTE_0 + 8},
+  {  4, NOTE_0 + 9},
+  { 32, NOTE_0 + 10},
+  { 38, NOTE_0 + 11},
+  { 40, NOTE_0 + 12}, // C1
+  { 31, NOTE_0 + 13},
+  { 37, NOTE_0 + 14},
+  { 35, NOTE_0 + 15},
+  { 41, NOTE_0 + 16},
+  { 39, NOTE_0 + 17},
+  { 30, NOTE_0 + 18},
+  { 42, NOTE_0 + 19},
+
+  { 50, VEL_0 + int(0)},
+  { 18, VEL_0 + int(1 * 127 / 12)},
+  { 19, VEL_0 + int(2 * 127 / 12)},
+  { 20, VEL_0 + int(3 * 127 / 12)},
+  { 21, VEL_0 + int(4 * 127 / 12)},
+  { 23, VEL_0 + int(5 * 127 / 12)},
+  { 22, VEL_0 + int(6 * 127 / 12)},
+  { 26, VEL_0 + int(7 * 127 / 12)},
+  { 28, VEL_0 + int(8 * 127 / 12)},
+  { 25, VEL_0 + int(9 * 127 / 12)},
+  { 29, VEL_0 + int(10 * 127 / 12)},
+  { 27, VEL_0 + int(11 * 127 / 12)},
+  { 24, VEL_0 + int(127)},
+
+  { 6, OCTAVE_MINUS },
+  { 7, OCTAVE_PLUS },
+  { 8, VELOCITY_MINUS },
+  { 9, VELOCITY_PLUS},
+  };
+  return ret;
+}
+
+// Use: https://kbdlayout.info/KBDUSX/scancodes
+// Also valid for windows
+static inline const std::map<int, int>& scancode_map_linux() {
+  static const std::map<int, int> ret{
+  { 0x1E, NOTE_0}, // C0
+  { 0x11, NOTE_0 + 1},
+  { 0x1F, NOTE_0 + 2},
+  { 0x12, NOTE_0 + 3},
+  { 0x20, NOTE_0 + 4},
+  { 0x21, NOTE_0 + 5},
+  { 0x14, NOTE_0 + 6},
+  { 0x22, NOTE_0 + 7},
+  { 0x15, NOTE_0 + 8},
+  { 0x23, NOTE_0 + 9},
+  { 0x16, NOTE_0 + 10},
+  { 0x24, NOTE_0 + 11},
+  { 0x25, NOTE_0 + 12}, // C1
+  { 0x18, NOTE_0 + 13},
+  { 0x26, NOTE_0 + 14},
+  { 0x19, NOTE_0 + 15},
+  { 0x27, NOTE_0 + 16},
+  { 0x28, NOTE_0 + 17},
+  { 0x1B, NOTE_0 + 18},
+  { 0x2B, NOTE_0 + 19},
+
+  { 0x29, VEL_0 + int(0)},
+  { 0x02, VEL_0 + int(1 * 127 / 12)},
+  { 0x03, VEL_0 + int(2 * 127 / 12)},
+  { 0x04, VEL_0 + int(3 * 127 / 12)},
+  { 0x05, VEL_0 + int(4 * 127 / 12)},
+  { 0x06, VEL_0 + int(5 * 127 / 12)},
+  { 0x07, VEL_0 + int(6 * 127 / 12)},
+  { 0x08, VEL_0 + int(7 * 127 / 12)},
+  { 0x09, VEL_0 + int(8 * 127 / 12)},
+  { 0x0A, VEL_0 + int(9 * 127 / 12)},
+  { 0x0B, VEL_0 + int(10 * 127 / 12)},
+  { 0x0C, VEL_0 + int(11 * 127 / 12)},
+  { 0x0D, VEL_0 + int(127)},
+
+  { 0x2C, OCTAVE_MINUS },
+  { 0x2D, OCTAVE_PLUS },
+  { 0x2E, VELOCITY_MINUS },
+  { 0x2F, VELOCITY_PLUS},
+  };
+  return ret;
+}
+// clang-format on
 /**
  * Used to set up keyboard input.
  * Your app should pass a function that will give you a callback
@@ -13,80 +135,20 @@
  */
 struct kbd_input_configuration
 {
+  // Note that on Linux with X11 scancodes should be offset by 8 (an X11 legacy quirk)
+  // ex.: pass QKeyEvent::nativeScanCode() - 8 to this API
   using scancode_callback = std::function<void(int)>;
+
   // First argument is on key press, second on key release
   std::function<void(scancode_callback, scancode_callback)> set_input_scancode_callbacks
-      = [](scancode_callback, scancode_callback) {};
-
-  enum kbd_event
-  {
-    NOTE_0 = 0x0,         // C
-    VEL_0 = NOTE_0 + 128, // Set velocity to 0
-    OCT_0 = VEL_0 + 128,  // Set octave to 0
-    OCTAVE_PLUS = OCT_0 + 128,
-    OCTAVE_MINUS,
-    VELOCITY_PLUS,
-    VELOCITY_MINUS,
-  };
-
-  // Default map
-  //
-  // ,---,---,---,---,---,---,---,---,---,---,---,---,---,-------,
-  // | V0| V1| V2| V3| V4| V5| V6| V7| V8| V9|V10|V11|V12| <-    |
-  // |---'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-----|
-  // | ->| |   | C#| D#|   | F#| G#| A#|   | C#| D#|   | F#|     |
-  // |-----',--',--',--',--',--',--',--',--',--',--',--',--'|    |
-  // | Caps | C | D | E | F | G | A | B | C | D | E | F | G |    |
-  // |----,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'---'----|
-  // | -^ |   | O-| O+| V-| V+|   |   |   |   |   |   |   ----^  |
-  // |----'-,-',--'--,'---'---'---'---'---'---'-,-'---',--,------|
-  // | ctrl |  | alt |                          |altgr |  | ctrl |
-  // '------'  '-----'--------------------------'------'  '------'
-
-  // clang-format off
-  // Use: https://kbdlayout.info/KBDUSX/scancodes
-  std::map<int, int> scancode_map{
-      { 0x1E, NOTE_0}, // C0
-      { 0x11, NOTE_0 + 1},
-      { 0x1F, NOTE_0 + 2},
-      { 0x12, NOTE_0 + 3},
-      { 0x20, NOTE_0 + 4},
-      { 0x21, NOTE_0 + 5},
-      { 0x14, NOTE_0 + 6},
-      { 0x22, NOTE_0 + 7},
-      { 0x15, NOTE_0 + 8},
-      { 0x23, NOTE_0 + 9},
-      { 0x16, NOTE_0 + 10},
-      { 0x24, NOTE_0 + 11},
-      { 0x25, NOTE_0 + 12}, // C1
-      { 0x18, NOTE_0 + 13}, 
-      { 0x26, NOTE_0 + 14}, 
-      { 0x19, NOTE_0 + 15},
-      { 0x27, NOTE_0 + 16},
-      { 0x28, NOTE_0 + 17},
-      { 0x1B, NOTE_0 + 18},
-      { 0x2B, NOTE_0 + 19},
-
-      { 0x29, VEL_0 + int(0)},
-      { 0x02, VEL_0 + int(1 * 127 / 12)},
-      { 0x03, VEL_0 + int(2 * 127 / 12)},
-      { 0x04, VEL_0 + int(3 * 127 / 12)},
-      { 0x05, VEL_0 + int(4 * 127 / 12)},
-      { 0x06, VEL_0 + int(5 * 127 / 12)},
-      { 0x07, VEL_0 + int(6 * 127 / 12)},
-      { 0x08, VEL_0 + int(7 * 127 / 12)},
-      { 0x09, VEL_0 + int(8 * 127 / 12)},
-      { 0x0A, VEL_0 + int(9 * 127 / 12)},
-      { 0x0B, VEL_0 + int(10 * 127 / 12)},
-      { 0x0C, VEL_0 + int(11 * 127 / 12)},
-      { 0x0D, VEL_0 + int(127)},
+      = [](scancode_callback, scancode_callback) { };
 
-      { 0x2C, OCTAVE_MINUS },
-      { 0x2D, OCTAVE_PLUS },
-      { 0x2E, VELOCITY_MINUS },
-      { 0x2F, VELOCITY_PLUS}, 
-  };
-  // clang-format on
+  std::map<int, int> scancode_map
+#if defined(__APPLE__)
+      = scancode_map_macos()
+#else
+      = scancode_map_linux()
+#endif
+      ;
 };
-
 }
diff --git a/vendor/libremidi/backends/keyboard/midi_in.hpp b/vendor/libremidi/backends/keyboard/midi_in.hpp
--- a/vendor/libremidi/backends/keyboard/midi_in.hpp
+++ b/vendor/libremidi/backends/keyboard/midi_in.hpp
@@ -45,7 +45,7 @@
 
   void on_keypress(int scancode)
   {
-    using kevent = kbd_input_configuration::kbd_event;
+    using kevent = libremidi::kbd_event;
 
     auto it = configuration.scancode_map.find(scancode);
     if (it == configuration.scancode_map.end())
@@ -88,7 +88,7 @@
 
   void on_keyrelease(int scancode)
   {
-    using kevent = kbd_input_configuration::kbd_event;
+    using kevent = libremidi::kbd_event;
 
     auto it = configuration.scancode_map.find(scancode);
     if (it == configuration.scancode_map.end())
diff --git a/vendor/libremidi/backends/network.hpp b/vendor/libremidi/backends/network.hpp
--- a/vendor/libremidi/backends/network.hpp
+++ b/vendor/libremidi/backends/network.hpp
@@ -5,6 +5,8 @@
 #include <libremidi/backends/net/midi_out.hpp>
 #include <libremidi/backends/net/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi::net
 {
 struct backend
@@ -16,8 +18,8 @@
   using midi_out_configuration = libremidi::net::dgram_output_configuration;
   using midi_observer_configuration = libremidi::net::net_observer_configuration;
   static const constexpr auto API = libremidi::API::NETWORK;
-  static const constexpr auto name = "network";
-  static const constexpr auto display_name = "Network";
+  static const constexpr std::string_view name = "network";
+  static const constexpr std::string_view display_name = "Network";
 
   static inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/network_ump.hpp b/vendor/libremidi/backends/network_ump.hpp
--- a/vendor/libremidi/backends/network_ump.hpp
+++ b/vendor/libremidi/backends/network_ump.hpp
@@ -4,6 +4,8 @@
 #include <libremidi/backends/net/midi_out.hpp>
 #include <libremidi/backends/net/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi::net_ump
 {
 struct backend
@@ -15,8 +17,8 @@
   using midi_out_configuration = libremidi::net_ump::dgram_output_configuration;
   using midi_observer_configuration = libremidi::net_ump::net_observer_configuration;
   static const constexpr auto API = libremidi::API::NETWORK_UMP;
-  static const constexpr auto name = "network (UMP)";
-  static const constexpr auto display_name = "Network (UMP)";
+  static const constexpr std::string_view name = "network (UMP)";
+  static const constexpr std::string_view display_name = "Network (UMP)";
 
   static inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/pipewire.hpp b/vendor/libremidi/backends/pipewire.hpp
--- a/vendor/libremidi/backends/pipewire.hpp
+++ b/vendor/libremidi/backends/pipewire.hpp
@@ -7,6 +7,8 @@
 #include <libremidi/backends/pipewire/midi_out.hpp>
 #include <libremidi/backends/pipewire/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi::pipewire
 {
 struct backend
@@ -18,8 +20,8 @@
   using midi_out_configuration = pipewire_output_configuration;
   using midi_observer_configuration = pipewire_observer_configuration;
   static const constexpr auto API = libremidi::API::PIPEWIRE;
-  static const constexpr auto name = "pipewire";
-  static const constexpr auto display_name = "PipeWire";
+  static const constexpr std::string_view name = "pipewire";
+  static const constexpr std::string_view display_name = "PipeWire";
 
   static inline bool available() noexcept
   {
diff --git a/vendor/libremidi/backends/winmidi.hpp b/vendor/libremidi/backends/winmidi.hpp
--- a/vendor/libremidi/backends/winmidi.hpp
+++ b/vendor/libremidi/backends/winmidi.hpp
@@ -3,6 +3,8 @@
 #include <libremidi/backends/winmidi/midi_out.hpp>
 #include <libremidi/backends/winmidi/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi::winmidi
 {
 struct backend
@@ -14,8 +16,8 @@
   using midi_out_configuration = winmidi::output_configuration;
   using midi_observer_configuration = winmidi::observer_configuration;
   static const constexpr auto API = libremidi::API::WINDOWS_MIDI_SERVICES;
-  static const constexpr auto name = "winmidi";
-  static const constexpr auto display_name = "Windows MIDI Services";
+  static const constexpr std::string_view name = "winmidi";
+  static const constexpr std::string_view display_name = "Windows MIDI Services";
 
   static constexpr inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/winmm.hpp b/vendor/libremidi/backends/winmm.hpp
--- a/vendor/libremidi/backends/winmm.hpp
+++ b/vendor/libremidi/backends/winmm.hpp
@@ -3,6 +3,8 @@
 #include <libremidi/backends/winmm/midi_out.hpp>
 #include <libremidi/backends/winmm/observer.hpp>
 
+#include <string_view>
+
 // Default for Windows is to add an identifier to the port names; this
 // flag can be defined (e.g. in your project file) to disable this behaviour.
 // #define LIBREMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
@@ -28,8 +30,8 @@
   using midi_out_configuration = winmm_output_configuration;
   using midi_observer_configuration = winmm_observer_configuration;
   static const constexpr auto API = libremidi::API::WINDOWS_MM;
-  static const constexpr auto name = "winmm";
-  static const constexpr auto display_name = "Windows Multimedia";
+  static const constexpr std::string_view name = "winmm";
+  static const constexpr std::string_view display_name = "Windows Multimedia";
 
   static constexpr inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/backends/winuwp.hpp b/vendor/libremidi/backends/winuwp.hpp
--- a/vendor/libremidi/backends/winuwp.hpp
+++ b/vendor/libremidi/backends/winuwp.hpp
@@ -3,6 +3,8 @@
 #include <libremidi/backends/winuwp/midi_out.hpp>
 #include <libremidi/backends/winuwp/observer.hpp>
 
+#include <string_view>
+
 namespace libremidi
 {
 struct winuwp_backend
@@ -14,8 +16,8 @@
   using midi_out_configuration = winuwp_output_configuration;
   using midi_observer_configuration = winuwp_observer_configuration;
   static const constexpr auto API = libremidi::API::WINDOWS_UWP;
-  static const constexpr auto name = "winuwp";
-  static const constexpr auto display_name = "Windows UWP";
+  static const constexpr std::string_view name = "winuwp";
+  static const constexpr std::string_view display_name = "Windows UWP";
 
   static constexpr inline bool available() noexcept { return true; }
 };
diff --git a/vendor/libremidi/configurations.hpp b/vendor/libremidi/configurations.hpp
--- a/vendor/libremidi/configurations.hpp
+++ b/vendor/libremidi/configurations.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <libremidi/api.hpp>
 #include <libremidi/backends/alsa_raw/config.hpp>
 #include <libremidi/backends/alsa_raw_ump/config.hpp>
 #include <libremidi/backends/alsa_seq/config.hpp>
@@ -14,11 +15,51 @@
 #include <libremidi/backends/winmidi/config.hpp>
 #include <libremidi/backends/winmm/config.hpp>
 #include <libremidi/backends/winuwp/config.hpp>
+#include <libremidi/config.hpp>
 
+#include <variant>
 namespace libremidi
 {
-
+struct unspecified_configuration
+{
+};
 struct dummy_configuration
 {
 };
+
+using input_api_configuration = std::variant<
+    unspecified_configuration, dummy_configuration, alsa_raw_input_configuration,
+    alsa_raw_ump::input_configuration, alsa_seq::input_configuration,
+    alsa_seq_ump::input_configuration, coremidi_input_configuration,
+    coremidi_ump::input_configuration, emscripten_input_configuration, jack_input_configuration,
+    kbd_input_configuration, libremidi::net::dgram_input_configuration,
+    libremidi::net_ump::dgram_input_configuration, pipewire_input_configuration,
+    winmidi::input_configuration, winmm_input_configuration, winuwp_input_configuration,
+    libremidi::API>;
+
+using output_api_configuration = std::variant<
+    unspecified_configuration, dummy_configuration, alsa_raw_output_configuration,
+    alsa_raw_ump::output_configuration, alsa_seq::output_configuration,
+    alsa_seq_ump::output_configuration, coremidi_output_configuration,
+    coremidi_ump::output_configuration, emscripten_output_configuration, jack_output_configuration,
+    libremidi::net::dgram_output_configuration, libremidi::net_ump::dgram_output_configuration,
+    pipewire_output_configuration, winmidi::output_configuration, winmm_output_configuration,
+    winuwp_output_configuration, libremidi::API>;
+
+using observer_api_configuration = std::variant<
+    unspecified_configuration, dummy_configuration, alsa_raw_observer_configuration,
+    alsa_raw_ump::observer_configuration, alsa_seq::observer_configuration,
+    alsa_seq_ump::observer_configuration, coremidi_observer_configuration,
+    coremidi_ump::observer_configuration, emscripten_observer_configuration,
+    jack_observer_configuration, libremidi::net::dgram_observer_configuration,
+    libremidi::net_ump::dgram_observer_configuration, pipewire_observer_configuration,
+    winmidi::observer_configuration, winmm_observer_configuration, winuwp_observer_configuration,
+    libremidi::API>;
+
+LIBREMIDI_EXPORT
+libremidi::API midi_api(const input_api_configuration& conf);
+LIBREMIDI_EXPORT
+libremidi::API midi_api(const output_api_configuration& conf);
+LIBREMIDI_EXPORT
+libremidi::API midi_api(const observer_api_configuration& conf);
 }
diff --git a/vendor/libremidi/defaults.hpp b/vendor/libremidi/defaults.hpp
--- a/vendor/libremidi/defaults.hpp
+++ b/vendor/libremidi/defaults.hpp
@@ -1,11 +1,11 @@
 #pragma once
 #include <libremidi/api.hpp>
 #include <libremidi/config.hpp>
+#include <libremidi/configurations.hpp>
 #include <libremidi/input_configuration.hpp>
 #include <libremidi/observer_configuration.hpp>
 #include <libremidi/output_configuration.hpp>
 
-#include <any>
 #include <optional>
 
 namespace libremidi
@@ -16,22 +16,22 @@
 
 //! Get the default midi in configuration object for a given API
 LIBREMIDI_EXPORT
-std::any midi_in_configuration_for(libremidi::API);
+input_api_configuration midi_in_configuration_for(libremidi::API);
 
 //! Get the default midi out configuration object for a given API
 LIBREMIDI_EXPORT
-std::any midi_out_configuration_for(libremidi::API);
+output_api_configuration midi_out_configuration_for(libremidi::API);
 
 //! Get the default observer configuration object for a given API
 LIBREMIDI_EXPORT
-std::any observer_configuration_for(libremidi::API);
+observer_api_configuration observer_configuration_for(libremidi::API);
 
 //! Get a matching midi in configuration object for a given observer instance.
 LIBREMIDI_EXPORT
-std::any midi_in_configuration_for(const libremidi::observer&);
+input_api_configuration midi_in_configuration_for(const libremidi::observer&);
 //! Get a matching midi out configuration object for a given observer instance.
 LIBREMIDI_EXPORT
-std::any midi_out_configuration_for(const libremidi::observer&);
+output_api_configuration midi_out_configuration_for(const libremidi::observer&);
 
 //! Get the default port for midi input (if any) in a given API.
 //! Note that this will only return a hardware / physical port.
@@ -47,15 +47,15 @@
 {
 //! Get a default configuration for creating a MIDI 1 input
 LIBREMIDI_EXPORT
-std::any in_default_configuration();
+input_api_configuration in_default_configuration();
 
 //! Get a default configuration for creating a MIDI 1 output
 LIBREMIDI_EXPORT
-std::any out_default_configuration();
+output_api_configuration out_default_configuration();
 
 //! Get a default configuration for creating a MIDI 1 observer
 LIBREMIDI_EXPORT
-std::any observer_default_configuration();
+observer_api_configuration observer_default_configuration();
 
 //! Get a default MIDI 1 input port for the default API of the system
 inline std::optional<input_port> in_default_port() noexcept
@@ -74,15 +74,15 @@
 {
 //! Get a default configuration for creating a MIDI 2 input
 LIBREMIDI_EXPORT
-std::any in_default_configuration();
+input_api_configuration in_default_configuration();
 
 //! Get a default configuration for creating a MIDI 2 output
 LIBREMIDI_EXPORT
-std::any out_default_configuration();
+output_api_configuration out_default_configuration();
 
 //! Get a default configuration for creating a MIDI 2 observer
 LIBREMIDI_EXPORT
-std::any observer_default_configuration();
+observer_api_configuration observer_default_configuration();
 
 //! Get a default MIDI 2 input port for the default API of the system
 inline std::optional<input_port> in_default_port() noexcept
diff --git a/vendor/libremidi/libremidi-c.cpp b/vendor/libremidi/libremidi-c.cpp
--- a/vendor/libremidi/libremidi-c.cpp
+++ b/vendor/libremidi/libremidi-c.cpp
@@ -44,14 +44,14 @@
   return LIBREMIDI_VERSION;
 }
 
-void libremidi_available_midi1_apis(void* ctx, void (*cb)(void* ctx, libremidi_api))
+void libremidi_midi1_available_apis(void* ctx, void (*cb)(void* ctx, libremidi_api))
 {
   if (!cb)
     return;
   libremidi::midi1::for_all_backends([=](auto b) { cb(ctx, b.API); });
 }
 
-void libremidi_available_midi2_apis(void* ctx, void (*cb)(void* ctx, libremidi_api))
+void libremidi_midi2_available_apis(void* ctx, void (*cb)(void* ctx, libremidi_api))
 {
   if (!cb)
     return;
diff --git a/vendor/libremidi/libremidi.cpp b/vendor/libremidi/libremidi.cpp
--- a/vendor/libremidi/libremidi.cpp
+++ b/vendor/libremidi/libremidi.cpp
@@ -53,14 +53,13 @@
   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 midi_api(const input_api_configuration& 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))
+    if (std::get_if<typename T::midi_in_configuration>(&conf))
     {
       ret = T::API;
     }
@@ -68,9 +67,34 @@
   return ret;
 }
 LIBREMIDI_INLINE
-std::any midi_in_configuration_for(libremidi::API api)
+libremidi::API midi_api(const output_api_configuration& conf)
 {
-  std::any ret;
+  libremidi::API ret = libremidi::API::UNSPECIFIED;
+  midi_any::for_all_backends([&]<typename T>(T) {
+    if (std::get_if<typename T::midi_out_configuration>(&conf))
+    {
+      ret = T::API;
+    }
+  });
+  return ret;
+}
+LIBREMIDI_INLINE
+libremidi::API midi_api(const observer_api_configuration& conf)
+{
+  libremidi::API ret = libremidi::API::UNSPECIFIED;
+  midi_any::for_all_backends([&]<typename T>(T) {
+    if (std::get_if<typename T::midi_observer_configuration>(&conf))
+    {
+      ret = T::API;
+    }
+  });
+  return ret;
+}
+
+LIBREMIDI_INLINE
+input_api_configuration midi_in_configuration_for(libremidi::API api)
+{
+  input_api_configuration ret;
   midi_any::for_backend(api, [&]<typename T>(T) {
     using conf_type = typename T::midi_in_configuration;
     ret = conf_type{};
@@ -79,9 +103,9 @@
 }
 
 LIBREMIDI_INLINE
-std::any midi_out_configuration_for(libremidi::API api)
+output_api_configuration midi_out_configuration_for(libremidi::API api)
 {
-  std::any ret;
+  output_api_configuration ret;
   midi_any::for_backend(api, [&]<typename T>(T) {
     using conf_type = typename T::midi_out_configuration;
     ret = conf_type{};
@@ -90,9 +114,9 @@
 }
 
 LIBREMIDI_INLINE
-std::any observer_configuration_for(libremidi::API api)
+observer_api_configuration observer_configuration_for(libremidi::API api)
 {
-  std::any ret;
+  observer_api_configuration ret;
   midi_any::for_backend(api, [&]<typename T>(T) {
     using conf_type = typename T::midi_observer_configuration;
     ret = conf_type{};
@@ -101,13 +125,13 @@
 }
 
 LIBREMIDI_INLINE
-std::any midi_in_configuration_for(const libremidi::observer& obs)
+input_api_configuration 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)
+output_api_configuration 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());
@@ -144,19 +168,19 @@
 namespace midi1
 {
 LIBREMIDI_INLINE
-std::any in_default_configuration()
+input_api_configuration in_default_configuration()
 {
   return midi_in_configuration_for(default_api());
 }
 
 LIBREMIDI_INLINE
-std::any out_default_configuration()
+output_api_configuration out_default_configuration()
 {
   return midi_out_configuration_for(default_api());
 }
 
 LIBREMIDI_INLINE
-std::any observer_default_configuration()
+observer_api_configuration observer_default_configuration()
 {
   return observer_configuration_for(default_api());
 }
@@ -165,19 +189,19 @@
 namespace midi2
 {
 LIBREMIDI_INLINE
-std::any in_default_configuration()
+input_api_configuration in_default_configuration()
 {
   return midi_in_configuration_for(default_api());
 }
 
 LIBREMIDI_INLINE
-std::any out_default_configuration()
+output_api_configuration out_default_configuration()
 {
   return midi_out_configuration_for(default_api());
 }
 
 LIBREMIDI_INLINE
-std::any observer_default_configuration()
+observer_api_configuration observer_default_configuration()
 {
   return observer_configuration_for(default_api());
 }
diff --git a/vendor/libremidi/libremidi.hpp b/vendor/libremidi/libremidi.hpp
--- a/vendor/libremidi/libremidi.hpp
+++ b/vendor/libremidi/libremidi.hpp
@@ -58,6 +58,7 @@
 */
 
 #include <libremidi/api.hpp>
+#include <libremidi/configurations.hpp>
 #include <libremidi/defaults.hpp>
 #include <libremidi/input_configuration.hpp>
 #include <libremidi/message.hpp>
@@ -68,7 +69,6 @@
   #include <midi/sysex.h>
   #include <midi/universal_packet.h>
 #endif
-#include <any>
 
 namespace libremidi
 {
@@ -84,7 +84,7 @@
   //!   such as jack_observer_configuration, winmm_observer_configuration, etc...
   //! * if no callbacks are passed, no secondary thread will be created unless absolutely necessary
   explicit observer(const observer_configuration& conf = {}) noexcept;
-  explicit observer(observer_configuration conf, std::any api_conf);
+  explicit observer(observer_configuration conf, observer_api_configuration api_conf);
   observer(const observer&) = delete;
   observer(observer&& other) noexcept;
   observer& operator=(const observer&) = delete;
@@ -111,7 +111,7 @@
   //! Construct a midi_in object with a configuration object for a specific MIDI 1 back-end
   //! see configuration.hpp for the available configuration types.
   //! An exception will be thrown if the requested back-end cannot be opened.
-  explicit midi_in(input_configuration conf, std::any api_conf);
+  explicit midi_in(input_configuration conf, input_api_configuration api_conf);
 
   //! Construct a midi_in object with the default MIDI 2 back-end for the platform
   explicit midi_in(ump_input_configuration conf) noexcept;
@@ -119,7 +119,7 @@
   //! Construct a midi_in object with a configuration object for a specific MIDI 2 back-end
   //! see configuration.hpp for the available configuration types.
   //! An exception will be thrown if the requested back-end cannot be opened.
-  explicit midi_in(ump_input_configuration conf, std::any api_conf);
+  explicit midi_in(ump_input_configuration conf, input_api_configuration api_conf);
 
   midi_in(const midi_in&) = delete;
   midi_in(midi_in&& other) noexcept;
@@ -170,7 +170,7 @@
   //! Construct a midi_out object with a configuration object for a specific back-end
   //! see configuration.hpp for the available configuration types.
   //! An exception will be thrown if the requested back-end cannot be opened.
-  explicit midi_out(output_configuration conf, std::any api_conf);
+  explicit midi_out(output_configuration conf, output_api_configuration api_conf);
 
   midi_out(const midi_out&) = delete;
   midi_out(midi_out&& other) noexcept;
diff --git a/vendor/libremidi/midi_in.cpp b/vendor/libremidi/midi_in.cpp
--- a/vendor/libremidi/midi_in.cpp
+++ b/vendor/libremidi/midi_in.cpp
@@ -61,14 +61,14 @@
 }
 
 static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>
-make_midi_in(auto base_conf, std::any api_conf, auto backends)
+make_midi_in(auto base_conf, input_api_configuration 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))
+    if (auto conf = std::get_if<typename T::midi_in_configuration>(&api_conf))
     {
       ptr = libremidi::make<typename T::midi_in>(std::move(base_conf), std::move(*conf));
       return true;
@@ -117,8 +117,9 @@
   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)
+static LIBREMIDI_INLINE std::unique_ptr<midi_in_api> make_midi1_in(
+    const input_configuration& base_conf, const input_api_configuration& api_conf,
+    libremidi::API api)
 {
   if (libremidi::is_midi1(api))
   {
@@ -133,13 +134,13 @@
 }
 
 static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>
-make_midi1_in(const input_configuration& base_conf, const std::any& api_conf)
+make_midi1_in(const input_configuration& base_conf, const input_api_configuration& api_conf)
 {
-  if (!api_conf.has_value())
+  if (std::get_if<unspecified_configuration>(&api_conf))
   {
     return make_midi1_in(base_conf);
   }
-  else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))
+  else if (auto api_p = std::get_if<libremidi::API>(&api_conf))
   {
     if (*api_p == libremidi::API::UNSPECIFIED)
     {
@@ -166,7 +167,7 @@
 }
 
 LIBREMIDI_INLINE
-midi_in::midi_in(input_configuration base_conf, std::any api_conf)
+midi_in::midi_in(input_configuration base_conf, input_api_configuration api_conf)
     : impl_{make_midi1_in(base_conf, api_conf)}
 {
   if (!impl_)
@@ -214,7 +215,8 @@
 }
 
 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)
+    const ump_input_configuration& base_conf, const input_api_configuration& api_conf,
+    libremidi::API api)
 {
   if (is_midi2(api))
   {
@@ -230,13 +232,13 @@
 }
 
 static LIBREMIDI_INLINE std::unique_ptr<midi_in_api>
-make_midi2_in(const ump_input_configuration& base_conf, const std::any& api_conf)
+make_midi2_in(const ump_input_configuration& base_conf, const input_api_configuration& api_conf)
 {
-  if (!api_conf.has_value())
+  if (std::get_if<unspecified_configuration>(&api_conf))
   {
     return make_midi2_in(base_conf);
   }
-  else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))
+  else if (auto api_p = std::get_if<libremidi::API>(&api_conf))
   {
     if (*api_p == libremidi::API::UNSPECIFIED)
     {
@@ -263,7 +265,7 @@
 }
 
 LIBREMIDI_INLINE
-midi_in::midi_in(ump_input_configuration base_conf, std::any api_conf)
+midi_in::midi_in(ump_input_configuration base_conf, input_api_configuration api_conf)
     : impl_{make_midi2_in(base_conf, api_conf)}
 {
   if (!impl_)
diff --git a/vendor/libremidi/midi_out.cpp b/vendor/libremidi/midi_out.cpp
--- a/vendor/libremidi/midi_out.cpp
+++ b/vendor/libremidi/midi_out.cpp
@@ -11,11 +11,11 @@
 namespace libremidi
 {
 static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>
-make_midi_out_impl(auto base_conf, std::any api_conf)
+make_midi_out_impl(auto base_conf, output_api_configuration 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))
+    if (auto conf = std::get_if<typename T::midi_out_configuration>(&api_conf))
     {
       ptr = libremidi::make<typename T::midi_out>(std::move(base_conf), std::move(*conf));
       return true;
@@ -58,13 +58,13 @@
 }
 
 static LIBREMIDI_INLINE std::unique_ptr<midi_out_api>
-make_midi_out(const output_configuration& base_conf, const std::any& api_conf)
+make_midi_out(const output_configuration& base_conf, const output_api_configuration& api_conf)
 {
-  if (!api_conf.has_value())
+  if (std::get_if<unspecified_configuration>(&api_conf))
   {
     return make_midi_out(base_conf);
   }
-  else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))
+  else if (auto api_p = std::get_if<libremidi::API>(&api_conf))
   {
     if (*api_p == libremidi::API::UNSPECIFIED)
     {
@@ -90,7 +90,7 @@
 }
 
 LIBREMIDI_INLINE
-midi_out::midi_out(output_configuration base_conf, std::any api_conf)
+midi_out::midi_out(output_configuration base_conf, output_api_configuration api_conf)
     : impl_{make_midi_out(base_conf, api_conf)}
 {
   if (!impl_)
diff --git a/vendor/libremidi/observer.cpp b/vendor/libremidi/observer.cpp
--- a/vendor/libremidi/observer.cpp
+++ b/vendor/libremidi/observer.cpp
@@ -37,11 +37,11 @@
   return std::make_unique<observer_dummy>(observer_configuration{}, dummy_configuration{});
 }
 
-LIBREMIDI_INLINE auto make_observer_impl(auto base_conf, std::any api_conf)
+LIBREMIDI_INLINE auto make_observer_impl(auto base_conf, observer_api_configuration 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))
+    if (auto conf = std::get_if<typename T::midi_observer_configuration>(&api_conf))
     {
       ptr = libremidi::make<typename T::midi_observer>(std::move(base_conf), std::move(*conf));
       return true;
@@ -53,13 +53,14 @@
   return ptr;
 }
 
-LIBREMIDI_INLINE std::unique_ptr<observer_api> make_observer(auto base_conf, std::any api_conf)
+LIBREMIDI_INLINE std::unique_ptr<observer_api>
+make_observer(auto base_conf, observer_api_configuration api_conf)
 {
-  if (!api_conf.has_value())
+  if (std::get_if<unspecified_configuration>(&api_conf))
   {
     return make_observer(base_conf);
   }
-  else if (auto api_p = std::any_cast<libremidi::API>(&api_conf))
+  else if (auto api_p = std::get_if<libremidi::API>(&api_conf))
   {
     if (*api_p == libremidi::API::UNSPECIFIED)
     {
@@ -84,7 +85,8 @@
 {
 }
 
-LIBREMIDI_INLINE observer::observer(observer_configuration base_conf, std::any api_conf)
+LIBREMIDI_INLINE
+observer::observer(observer_configuration base_conf, observer_api_configuration api_conf)
     : impl_{make_observer(base_conf, api_conf)}
 {
   if (!impl_)
diff --git a/vendor/libremidi/shared_context.hpp b/vendor/libremidi/shared_context.hpp
--- a/vendor/libremidi/shared_context.hpp
+++ b/vendor/libremidi/shared_context.hpp
@@ -1,8 +1,6 @@
 #pragma once
 #include <libremidi/api.hpp>
-#include <libremidi/config.hpp>
-
-#include <any>
+#include <libremidi/configurations.hpp>
 
 namespace libremidi
 {
@@ -24,7 +22,9 @@
 struct shared_configurations
 {
   std::shared_ptr<shared_context> context;
-  std::any observer, in, out;
+  observer_api_configuration observer;
+  input_api_configuration in;
+  output_api_configuration out;
 };
 
 LIBREMIDI_EXPORT
