diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,6 +2,11 @@
 
 Haskell wrapper for [RtMidi](http://www.music.mcgill.ca/~gary/rtmidi/), the lightweight, cross-platform MIDI I/O library.
 
+## Version
+
+The version of this library does not correspond to the RtMidi version (sorry). You can look at the `justfile` to see that we vendor sources
+from version `5.0.0`, and we apply a small patch to fix API names (`rtmidi.patch`).
+
 ## How to use
 
 See [Hackage](https://hackage.haskell.org/package/RtMidi) for the latest released version and add `RtMidi` and `vector` to your library `build-depends`.
@@ -14,15 +19,16 @@
 
 This project is tested with Cabal (latest versions of last two compiler lines) and Stack (latest LTS).
 
-You can get started with development like so:
+You can get started with development like so (if you have `just` installed):
 
     # Build and test on OSX CoreMIDI support or Linux with ALSA support.
     # (You can also manually invoke stack and pass `--flag RtMidi:jack` for Jack support.)
-    make test
+    just test
 
     # Print information about all accessible MIDI devices and all compiled APIs.
-    make example-report
+    just example-report
 
 ## TODO
 
 * See if there is a way to autodetect Jack in the Cabal file.
+* See if there is a way to make OSX not take 3 seconds when initializing the MIDI subsystem.
diff --git a/RtMidi.cabal b/RtMidi.cabal
--- a/RtMidi.cabal
+++ b/RtMidi.cabal
@@ -1,6 +1,6 @@
 cabal-version:       3.0
 name:                RtMidi
-version:             0.6.1.0
+version:             0.7.0.0
 synopsis:            Haskell wrapper for RtMidi, the lightweight, cross-platform MIDI I/O library.
 description:         Please see the README on GitHub at <https://github.com/riottracker/RtMidi#readme>
 category:            Sound
@@ -46,21 +46,21 @@
 
   if os(linux)
     if flag(alsa) && flag(jack)
-      cxx-options:      -D__LINUX_ALSA__ -D__UNIX_JACK__
+      cxx-options:      -std=c++11 -D__LINUX_ALSA__ -D__UNIX_JACK__
       extra-libraries:  asound pthread jack
     if flag(alsa) && !flag(jack)
-      cxx-options:      -D__LINUX_ALSA__
+      cxx-options:      -std=c++11 -D__LINUX_ALSA__
       extra-libraries:  asound pthread
     if !flag(alsa) && flag(jack)
-      cxx-options:      -D__UNIX_JACK__
+      cxx-options:      -std=c++11 -D__UNIX_JACK__
       extra-libraries:  jack
 
   if os(darwin)
     if flag(jack)
-      cxx-options:      -D__MACOSX_CORE__ -D__UNIX_JACK__
+      cxx-options:      -std=c++11 -DTARGET_OS_IPHONE=0-D__MACOSX_CORE__ -D__UNIX_JACK__
       extra-libraries:  jack
     else
-      cxx-options:    -D__MACOSX_CORE__
+      cxx-options:    -std=c++11 -D__MACOSX_CORE__
     frameworks:       CoreMIDI CoreAudio CoreFoundation
     -- NOTE(ejconlon) This is to make the c ffi wrapper actually catch
     -- the c++ exceptions instead of simply aborting.
@@ -70,7 +70,7 @@
     ghc-options:       -pgmc=clang++
 
   if os(mingw32)
-    cxx-options:       -D__WINDOWS_MM__
+    cxx-options:       -std=c++11 -D__WINDOWS_MM__
     extra-libraries:   winmm
 
 executable rtmidi-callback
diff --git a/Sound/RtMidi.hs b/Sound/RtMidi.hs
--- a/Sound/RtMidi.hs
+++ b/Sound/RtMidi.hs
@@ -46,8 +46,8 @@
 import qualified Data.Vector.Storable as VS
 import qualified Data.Vector.Storable.Mutable as VSM
 import Data.Word (Word8)
-import Foreign (FunPtr, Ptr, Storable (..), alloca, allocaArray, nullPtr, peekArray)
-import Foreign.C (CDouble (..), CInt (..), CSize, CString, CUChar (..), peekCString, withCString)
+import Foreign (FunPtr, Ptr, Storable (..), alloca, allocaArray, allocaBytes, nullPtr, peekArray)
+import Foreign.C (CDouble (..), CInt (..), CSize, CString, CUChar (..), peekCString, peekCStringLen, withCString)
 import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr)
 import GHC.Generics (Generic)
 import Sound.RtMidi.Foreign
@@ -85,6 +85,10 @@
 defaultMessageSize :: Int
 defaultMessageSize = 4
 
+-- The max length of port names returned from 'rtmidi_get_port_name'
+maxPortNameLength :: Int
+maxPortNameLength = 256
+
 -- | Allows us to discriminate in/out functions in generic contexts
 data DeviceType
   = InputDeviceType
@@ -205,12 +209,15 @@
 -- 'Nothing' is returned if an invalid port specifier is provided.
 portName :: IsDevice d => d -> Int -> IO (Maybe String)
 portName d n = withDevicePtrUnguarded d $ \dptr -> do
-  x <- rtmidi_get_port_name dptr (toEnum n)
-  guardError dptr
-  s <- peekCString x
-  case s of
-    [] -> pure Nothing
-    _ -> pure (Just s)
+  alloca $ \lenPtr -> do
+    poke lenPtr (fromIntegral maxPortNameLength)
+    allocaBytes maxPortNameLength $ \namePtr -> do
+      used <- rtmidi_get_port_name dptr (toEnum n) namePtr lenPtr
+      guardError dptr
+      s <- peekCStringLen (namePtr, fromIntegral used)
+      case s of
+        [] -> pure Nothing
+        _ -> pure (Just s)
 
 -- | Convenience function to list ports.
 --
diff --git a/Sound/RtMidi/Foreign.hsc b/Sound/RtMidi/Foreign.hsc
--- a/Sound/RtMidi/Foreign.hsc
+++ b/Sound/RtMidi/Foreign.hsc
@@ -101,7 +101,7 @@
   rtmidi_get_port_count :: Ptr Wrapper -> IO CUInt
 
 foreign import ccall "rtmidi_c.h rtmidi_get_port_name"
-  rtmidi_get_port_name :: Ptr Wrapper -> CUInt -> IO CString
+  rtmidi_get_port_name :: Ptr Wrapper -> CUInt -> CString -> Ptr CInt -> IO CInt
 
 foreign import ccall "rtmidi_c.h rtmidi_in_cancel_callback"
   rtmidi_in_cancel_callback :: Ptr Wrapper -> IO ()
diff --git a/rtmidi/RtMidi.cpp b/rtmidi/RtMidi.cpp
--- a/rtmidi/RtMidi.cpp
+++ b/rtmidi/RtMidi.cpp
@@ -9,7 +9,7 @@
     RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/
 
     RtMidi: realtime MIDI i/o C++ classes
-    Copyright (c) 2003-2019 Gary P. Scavone
+    Copyright (c) 2003-2021 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -40,11 +40,31 @@
 #include "RtMidi.h"
 #include <sstream>
 
-#if defined(__MACOSX_CORE__)
-  #if TARGET_OS_IPHONE
+#if (TARGET_OS_IPHONE == 1)
+
     #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
     #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
-  #endif
+
+    #include <mach/mach_time.h>
+    class CTime2nsFactor
+    {
+    public:
+        CTime2nsFactor()
+        {
+            mach_timebase_info_data_t tinfo;
+            mach_timebase_info(&tinfo);
+            Factor = (double)tinfo.numer / tinfo.denom;
+        }
+        static double Factor;
+    };
+    double CTime2nsFactor::Factor;
+    static CTime2nsFactor InitTime2nsFactor;
+    #undef AudioGetCurrentHostTime
+    #undef AudioConvertHostTimeToNanos
+  #define AudioGetCurrentHostTime (uint64_t) mach_absolute_time
+  #define AudioConvertHostTimeToNanos(t) t *CTime2nsFactor::Factor
+  #define EndianS32_BtoN(n) n
+
 #endif
 
 // Default for Windows is to add an identifier to the port names; this
@@ -57,11 +77,12 @@
 //
 // **************************************************************** //
 
-#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__)
+#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(TARGET_IPHONE_OS) && !defined(__WEB_MIDI_API__)
   #define __RTMIDI_DUMMY__
 #endif
 
 #if defined(__MACOSX_CORE__)
+#include <CoreMIDI/CoreMIDI.h>
 
 class MidiInCore: public MidiInApi
 {
@@ -78,6 +99,7 @@
   std::string getPortName( unsigned int portNumber );
 
  protected:
+  MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
   void initialize( const std::string& clientName );
 };
 
@@ -97,6 +119,7 @@
   void sendMessage( const unsigned char *message, size_t size );
 
  protected:
+  MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
   void initialize( const std::string& clientName );
 };
 
@@ -231,6 +254,57 @@
 
 #endif
 
+#if defined(__WEB_MIDI_API__)
+
+class MidiInWeb : public MidiInApi
+{
+  std::string client_name{};
+  std::string web_midi_id{};
+  int open_port_number{-1};
+
+ public:
+  MidiInWeb(const std::string &/*clientName*/, unsigned int queueSizeLimit );
+  ~MidiInWeb( void );
+  RtMidi::Api getCurrentApi( void ) { return RtMidi::WEB_MIDI_API; };
+  void openPort( unsigned int portNumber, const std::string &portName );
+  void openVirtualPort( const std::string &portName );
+  void closePort( void );
+  void setClientName( const std::string &clientName );
+  void setPortName( const std::string &portName );
+  unsigned int getPortCount( void );
+  std::string getPortName( unsigned int portNumber );
+
+  void onMidiMessage( uint8_t* data, double domHishResTimeStamp );
+
+ protected:
+  void initialize( const std::string& clientName );
+};
+
+class MidiOutWeb: public MidiOutApi
+{
+  std::string client_name{};
+  std::string web_midi_id{};
+  int open_port_number{-1};
+
+ public:
+  MidiOutWeb( const std::string &clientName );
+  ~MidiOutWeb( void );
+  RtMidi::Api getCurrentApi( void ) { return RtMidi::WEB_MIDI_API; };
+  void openPort( unsigned int portNumber, const std::string &portName );
+  void openVirtualPort( const std::string &portName );
+  void closePort( void );
+  void setClientName( const std::string &clientName );
+  void setPortName( const std::string &portName );
+  unsigned int getPortCount( void );
+  std::string getPortName( unsigned int portNumber );
+  void sendMessage( const unsigned char *message, size_t size );
+
+ protected:
+  void initialize( const std::string& clientName );
+};
+
+#endif
+
 #if defined(__RTMIDI_DUMMY__)
 
 class MidiInDummy: public MidiInApi
@@ -285,6 +359,11 @@
   rtapi_ = 0;
 }
 
+RtMidi::RtMidi(RtMidi&& other) noexcept {
+    rtapi_ = other.rtapi_;
+    other.rtapi_ = nullptr;
+}
+
 std::string RtMidi :: getVersion( void ) throw()
 {
   return std::string( RTMIDI_VERSION );
@@ -300,6 +379,7 @@
   { "jack"        , "Jack" },
   { "winmm"       , "Windows MultiMedia" },
   { "dummy"       , "Dummy" },
+  { "web"         , "Web MIDI API" },
 };
 const unsigned int rtmidi_num_api_names =
   sizeof(rtmidi_api_names)/sizeof(rtmidi_api_names[0]);
@@ -319,6 +399,9 @@
 #if defined(__WINDOWS_MM__)
   RtMidi::WINDOWS_MM,
 #endif
+#if defined(__WEB_MIDI_API__)
+  RtMidi::WEB_MIDI_API,
+#endif
 #if defined(__RTMIDI_DUMMY__)
   RtMidi::RTMIDI_DUMMY,
 #endif
@@ -344,14 +427,14 @@
 
 std::string RtMidi :: getApiName( RtMidi::Api api )
 {
-  if (api < 0 || api >= RtMidi::NUM_APIS)
+  if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS)
     return "";
   return rtmidi_api_names[api][0];
 }
 
 std::string RtMidi :: getApiDisplayName( RtMidi::Api api )
 {
-  if (api < 0 || api >= RtMidi::NUM_APIS)
+  if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS)
     return "Unknown";
   return rtmidi_api_names[api][1];
 }
@@ -401,6 +484,10 @@
   if ( api == MACOSX_CORE )
     rtapi_ = new MidiInCore( clientName, queueSizeLimit );
 #endif
+#if defined(__WEB_MIDI_API__)
+    if ( api == WEB_MIDI_API )
+    rtapi_ = new MidiInWeb( clientName, queueSizeLimit );
+#endif
 #if defined(__RTMIDI_DUMMY__)
   if ( api == RTMIDI_DUMMY )
     rtapi_ = new MidiInDummy( clientName, queueSizeLimit );
@@ -469,6 +556,10 @@
   if ( api == MACOSX_CORE )
     rtapi_ = new MidiOutCore( clientName );
 #endif
+#if defined(__WEB_MIDI_API__)
+    if ( api == WEB_MIDI_API )
+    rtapi_ = new MidiOutWeb( clientName );
+#endif
 #if defined(__RTMIDI_DUMMY__)
   if ( api == RTMIDI_DUMMY )
     rtapi_ = new MidiOutDummy( clientName );
@@ -634,6 +725,12 @@
   return timeStamp;
 }
 
+void MidiInApi :: setBufferSize( unsigned int size, unsigned int count )
+{
+    inputData_.bufferSize = size;
+    inputData_.bufferCount = count;
+}
+
 unsigned int MidiInApi::MidiQueue::size( unsigned int *__back,
                                          unsigned int *__front )
 {
@@ -716,10 +813,11 @@
 // MIDI input.  We convert the system specific time stamps to delta
 // time values.
 
-// OS-X CoreMIDI header files.
-#include <CoreMIDI/CoreMIDI.h>
-#include <CoreAudio/HostTime.h>
-#include <CoreServices/CoreServices.h>
+// These are not available on iOS.
+#if (TARGET_OS_IPHONE == 0)
+  #include <CoreAudio/HostTime.h>
+  #include <CoreServices/CoreServices.h>
+#endif
 
 // A structure to hold variables related to the CoreMIDI API
 // implementation.
@@ -732,6 +830,20 @@
   MIDISysexSendRequest sysexreq;
 };
 
+static MIDIClientRef CoreMidiClientSingleton = 0;
+
+void RtMidi_setCoreMidiClientSingleton(MIDIClientRef client){
+  CoreMidiClientSingleton = client;
+}
+
+void RtMidi_disposeCoreMidiClientSingleton(){
+  if (CoreMidiClientSingleton == 0){
+    return;
+  }
+  MIDIClientDispose( CoreMidiClientSingleton );
+  CoreMidiClientSingleton = 0;
+}
+
 //*********************************************************************//
 //  API: OS-X
 //  Class Definitions: MidiInCore
@@ -899,24 +1011,37 @@
 
   // Cleanup.
   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
-  MIDIClientDispose( data->client );
   if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
   delete data;
 }
 
+MIDIClientRef MidiInCore::getCoreMidiClientSingleton(const std::string& clientName) throw() {
+
+  if (CoreMidiClientSingleton == 0){
+      // Set up our client.
+      MIDIClientRef client;
+
+      CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
+      OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
+      if ( result != noErr ) {
+        std::ostringstream ost;
+        ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
+        errorString_ = ost.str();
+        error( RtMidiError::DRIVER_ERROR, errorString_ );
+        return 0;
+      }
+      CFRelease( name );
+
+      CoreMidiClientSingleton = client;
+  }
+
+  return CoreMidiClientSingleton;
+}
+
 void MidiInCore :: initialize( const std::string& clientName )
 {
   // Set up our client.
-  MIDIClientRef client;
-  CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
-  OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
-  if ( result != noErr ) {
-    std::ostringstream ost;
-    ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
-    errorString_ = ost.str();
-    error( RtMidiError::DRIVER_ERROR, errorString_ );
-    return;
-  }
+  MIDIClientRef client = getCoreMidiClientSingleton(clientName);
 
   // Save our api-specific connection information.
   CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
@@ -924,7 +1049,6 @@
   data->endpoint = 0;
   apiData_ = (void *) data;
   inputData_.apiData = (void *) data;
-  CFRelease( name );
 }
 
 void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName )
@@ -960,7 +1084,6 @@
   CFRelease( portNameRef );
 
   if ( result != noErr ) {
-    MIDIClientDispose( data->client );
     errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
     return;
@@ -970,7 +1093,6 @@
   MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
   if ( endpoint == 0 ) {
     MIDIPortDispose( port );
-    MIDIClientDispose( data->client );
     errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
     return;
@@ -980,7 +1102,6 @@
   result = MIDIPortConnectSource( port, endpoint, NULL );
   if ( result != noErr ) {
     MIDIPortDispose( port );
-    MIDIClientDispose( data->client );
     errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
     return;
@@ -1068,6 +1189,11 @@
     CFRelease( str );
   }
 
+  // some MIDI devices have a leading space in endpoint name. trim
+  CFStringRef space = CFStringCreateWithCString(NULL, " ", kCFStringEncodingUTF8);
+  CFStringTrim(result, space);
+  CFRelease(space);
+
   MIDIEntityRef entity = 0;
   MIDIEndpointGetEntity( endpoint, &entity );
   if ( entity == 0 )
@@ -1223,31 +1349,43 @@
 
   // Cleanup.
   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
-  MIDIClientDispose( data->client );
   if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
   delete data;
 }
 
+MIDIClientRef MidiOutCore::getCoreMidiClientSingleton(const std::string& clientName) throw() {
+
+  if (CoreMidiClientSingleton == 0){
+      // Set up our client.
+      MIDIClientRef client;
+
+      CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
+      OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
+      if ( result != noErr ) {
+        std::ostringstream ost;
+        ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
+        errorString_ = ost.str();
+        error( RtMidiError::DRIVER_ERROR, errorString_ );
+        return 0;
+      }
+      CFRelease( name );
+
+      CoreMidiClientSingleton = client;
+  }
+
+  return CoreMidiClientSingleton;
+}
+
 void MidiOutCore :: initialize( const std::string& clientName )
 {
   // Set up our client.
-  MIDIClientRef client;
-  CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
-  OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
-  if ( result != noErr ) {
-    std::ostringstream ost;
-    ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
-    errorString_ = ost.str();
-    error( RtMidiError::DRIVER_ERROR, errorString_ );
-    return;
-  }
+  MIDIClientRef client = getCoreMidiClientSingleton(clientName);
 
   // Save our api-specific connection information.
   CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
   data->client = client;
   data->endpoint = 0;
   apiData_ = (void *) data;
-  CFRelease( name );
 }
 
 unsigned int MidiOutCore :: getPortCount()
@@ -1310,7 +1448,6 @@
   OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port );
   CFRelease( portNameRef );
   if ( result != noErr ) {
-    MIDIClientDispose( data->client );
     errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
     return;
@@ -1320,7 +1457,6 @@
   MIDIEndpointRef destination = MIDIGetDestination( portNumber );
   if ( destination == 0 ) {
     MIDIPortDispose( port );
-    MIDIClientDispose( data->client );
     errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
     return;
@@ -1402,50 +1538,54 @@
     return;
   }
 
-  MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
-  OSStatus result;
-
   if ( message[0] != 0xF0 && nBytes > 3 ) {
     errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
     error( RtMidiError::WARNING, errorString_ );
     return;
   }
 
-  Byte buffer[nBytes+(sizeof( MIDIPacketList ))];
+  MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
+  OSStatus result;
+
+  ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes;
+  Byte buffer[bufsize+16]; // pad for other struct members
   ByteCount listSize = sizeof( buffer );
   MIDIPacketList *packetList = (MIDIPacketList*)buffer;
-  MIDIPacket *packet = MIDIPacketListInit( packetList );
 
   ByteCount remainingBytes = nBytes;
-  while ( remainingBytes && packet ) {
-    ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket
+  while ( remainingBytes ) {
+    MIDIPacket *packet = MIDIPacketListInit( packetList );
+    // A MIDIPacketList can only contain a maximum of 64K of data, so if our message is longer,
+    // break it up into chunks of 64K or less and send out as a MIDIPacketList with only one
+    // MIDIPacket. Here, we reuse the memory allocated above on the stack for all.
+    ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes;
     const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes];
     packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr );
     remainingBytes -= bytesForPacket;
-  }
 
-  if ( !packet ) {
-    errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
-    error( RtMidiError::DRIVER_ERROR, errorString_ );
-    return;
-  }
+    if ( !packet ) {
+      errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
+      error( RtMidiError::DRIVER_ERROR, errorString_ );
+      return;
+    }
 
-  // Send to any destinations that may have connected to us.
-  if ( data->endpoint ) {
-    result = MIDIReceived( data->endpoint, packetList );
-    if ( result != noErr ) {
-      errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
-      error( RtMidiError::WARNING, errorString_ );
+    // Send to any destinations that may have connected to us.
+    if ( data->endpoint ) {
+      result = MIDIReceived( data->endpoint, packetList );
+      if ( result != noErr ) {
+        errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
+        error( RtMidiError::WARNING, errorString_ );
+      }
     }
-  }
 
-  // And send to an explicit destination port if we're connected.
-  if ( connected_ ) {
-    result = MIDISend( data->port, data->destinationId, packetList );
-    if ( result != noErr ) {
-      errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
-      error( RtMidiError::WARNING, errorString_ );
+    // And send to an explicit destination port if we're connected.
+    if ( connected_ ) {
+      result = MIDISend( data->port, data->destinationId, packetList );
+      if ( result != noErr ) {
+        errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
+        error( RtMidiError::WARNING, errorString_ );
+      }
     }
   }
 }
@@ -1487,6 +1627,7 @@
   snd_seq_port_subscribe_t *subscription;
   snd_midi_event_t *coder;
   unsigned int bufferSize;
+  unsigned int requestedBufferSize;
   unsigned char *buffer;
   pthread_t thread;
   pthread_t dummy_thread_id;
@@ -1517,7 +1658,6 @@
 
   snd_seq_event_t *ev;
   int result;
-  apiData->bufferSize = 32;
   result = snd_midi_event_new( 0, &apiData->coder );
   if ( result < 0 ) {
     data->doInput = false;
@@ -1769,6 +1909,7 @@
   data->thread = data->dummy_thread_id;
   data->trigger_fds[0] = -1;
   data->trigger_fds[1] = -1;
+  data->bufferSize = inputData_.bufferSize;
   apiData_ = (void *) data;
   inputData_.apiData = (void *) data;
 
@@ -2293,7 +2434,7 @@
 
 void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
 {
-  int result;
+  long result;
   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   unsigned int nBytes = static_cast<unsigned int> (size);
   if ( nBytes > data->bufferSize ) {
@@ -2313,25 +2454,38 @@
     }
   }
 
-  snd_seq_event_t ev;
-  snd_seq_ev_clear( &ev );
-  snd_seq_ev_set_source( &ev, data->vport );
-  snd_seq_ev_set_subs( &ev );
-  snd_seq_ev_set_direct( &ev );
   for ( unsigned int i=0; i<nBytes; ++i ) data->buffer[i] = message[i];
-  result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
-  if ( result < (int)nBytes ) {
-    errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
-    error( RtMidiError::WARNING, errorString_ );
-    return;
-  }
 
-  // Send the event.
-  result = snd_seq_event_output( data->seq, &ev );
-  if ( result < 0 ) {
-    errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port.";
-    error( RtMidiError::WARNING, errorString_ );
-    return;
+  unsigned int offset = 0;
+  while (offset < nBytes) {
+    snd_seq_event_t ev;
+    snd_seq_ev_clear( &ev );
+    snd_seq_ev_set_source( &ev, data->vport );
+    snd_seq_ev_set_subs( &ev );
+    snd_seq_ev_set_direct( &ev );
+    result = snd_midi_event_encode( data->coder, data->buffer + offset,
+                                    (long)(nBytes - offset), &ev );
+    if ( result < 0 ) {
+      errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
+      error( RtMidiError::WARNING, errorString_ );
+      return;
+    }
+
+    if ( ev.type == SND_SEQ_EVENT_NONE ) {
+      errorString_ = "MidiOutAlsa::sendMessage: incomplete message!";
+      error( RtMidiError::WARNING, errorString_ );
+      return;
+    }
+
+    offset += result;
+
+    // Send the event.
+    result = snd_seq_event_output( data->seq, &ev );
+    if ( result < 0 ) {
+      errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port.";
+      error( RtMidiError::WARNING, errorString_ );
+      return;
+    }
   }
   snd_seq_drain_output( data->seq );
 }
@@ -2386,9 +2540,6 @@
   return u8str;
 }
 
-#define  RT_SYSEX_BUFFER_SIZE 1024
-#define  RT_SYSEX_BUFFER_COUNT 4
-
 // A structure to hold variables related to the CoreMIDI API
 // implementation.
 struct WinMidiData {
@@ -2396,7 +2547,7 @@
   HMIDIOUT outHandle;  // Handle to Midi Output Device
   DWORD lastTime;
   MidiInApi::MidiMessage message;
-  LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];
+  std::vector<LPMIDIHDR> sysexBuffer;
   CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
 };
 
@@ -2576,10 +2727,11 @@
   }
 
   // Allocate and init the sysex buffers.
-  for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
+  data->sysexBuffer.resize( inputData_.bufferCount );
+  for ( int i=0; i < inputData_.bufferCount; ++i ) {
     data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
-    data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
-    data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
+    data->sysexBuffer[i]->lpData = new char[ inputData_.bufferSize ];
+    data->sysexBuffer[i]->dwBufferLength = inputData_.bufferSize;
     data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
     data->sysexBuffer[i]->dwFlags = 0;
 
@@ -2630,7 +2782,7 @@
     midiInReset( data->inHandle );
     midiInStop( data->inHandle );
 
-    for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
+    for ( int i=0; i < data->sysexBuffer.size(); ++i ) {
       int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
       delete [] data->sysexBuffer[i]->lpData;
       delete [] data->sysexBuffer[i];
@@ -2811,7 +2963,10 @@
 {
   if ( connected_ ) {
     WinMidiData *data = static_cast<WinMidiData *> (apiData_);
-    midiOutReset( data->outHandle );
+    // Disabled because midiOutReset triggers 0x7b (if any note was ON) and 0x79 "Reset All
+    // Controllers" (to all 16 channels) CC messages which is undesirable (see issue #222)
+    // midiOutReset( data->outHandle );
+
     midiOutClose( data->outHandle );
     data->outHandle = 0;
     connected_ = false;
@@ -2936,6 +3091,7 @@
 #include <jack/jack.h>
 #include <jack/midiport.h>
 #include <jack/ringbuffer.h>
+#include <pthread.h>
 #ifdef HAVE_SEMAPHORE
   #include <semaphore.h>
 #endif
@@ -2945,8 +3101,8 @@
 struct JackMidiData {
   jack_client_t *client;
   jack_port_t *port;
-  jack_ringbuffer_t *buffSize;
-  jack_ringbuffer_t *buffMessage;
+  jack_ringbuffer_t *buff;
+  int buffMaxWrite; // actual writable size, usually 1 less than ringbuffer
   jack_time_t lastTime;
 #ifdef HAVE_SEMAPHORE
   sem_t sem_cleanup;
@@ -3101,6 +3257,8 @@
 
   if ( data->port == NULL ) {
     errorString_ = "MidiInJack::openPort: JACK error creating port";
+    if (portName.size() >= (size_t)jack_port_name_size())
+        errorString_ += " (port name too long?)";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
     return;
   }
@@ -3123,6 +3281,8 @@
 
   if ( data->port == NULL ) {
     errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";
+    if (portName.size() >= (size_t)jack_port_name_size())
+        errorString_ += " (port name too long?)";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
   }
 }
@@ -3226,11 +3386,15 @@
   void *buff = jack_port_get_buffer( data->port, nframes );
   jack_midi_clear_buffer( buff );
 
-  while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
-    jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof( space ) );
-    midiData = jack_midi_event_reserve( buff, 0, space );
+  while ( jack_ringbuffer_peek( data->buff, (char *) &space, sizeof( space ) ) == sizeof(space) &&
+          jack_ringbuffer_read_space( data->buff ) >= sizeof(space) + space ) {
+    jack_ringbuffer_read_advance( data->buff, sizeof(space) );
 
-    jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );
+    midiData = jack_midi_event_reserve( buff, 0, space );
+    if ( midiData )
+        jack_ringbuffer_read( data->buff, (char *) midiData, (size_t) space );
+    else
+        jack_ringbuffer_read_advance( data->buff, (size_t) space );
   }
 
 #ifdef HAVE_SEMAPHORE
@@ -3269,8 +3433,8 @@
     return;
 
   // Initialize output ringbuffers
-  data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
-  data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
+  data->buff = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
+  data->buffMaxWrite = (int) jack_ringbuffer_write_space( data->buff );
 
   // Initialize JACK client
   if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) {
@@ -3289,8 +3453,7 @@
   MidiOutJack::closePort();
 
   // Cleanup
-  jack_ringbuffer_free( data->buffSize );
-  jack_ringbuffer_free( data->buffMessage );
+  jack_ringbuffer_free( data->buff );
   if ( data->client ) {
     jack_client_close( data->client );
   }
@@ -3316,6 +3479,8 @@
 
   if ( data->port == NULL ) {
     errorString_ = "MidiOutJack::openPort: JACK error creating port";
+    if (portName.size() >= (size_t)jack_port_name_size())
+        errorString_ += " (port name too long?)";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
     return;
   }
@@ -3338,6 +3503,8 @@
 
   if ( data->port == NULL ) {
     errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";
+    if (portName.size() >= (size_t)jack_port_name_size())
+        errorString_ += " (port name too long?)";
     error( RtMidiError::DRIVER_ERROR, errorString_ );
   }
 }
@@ -3437,9 +3604,331 @@
   int nBytes = static_cast<int>(size);
   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
 
+  if ( size + sizeof(nBytes) > (size_t) data->buffMaxWrite )
+      return;
+
+  while ( jack_ringbuffer_write_space(data->buff) < sizeof(nBytes) + size )
+      pthread_yield();
+
   // Write full message to buffer
-  jack_ringbuffer_write( data->buffMessage, ( const char * ) message, nBytes );
-  jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
+  jack_ringbuffer_write( data->buff, ( char * ) &nBytes, sizeof( nBytes ) );
+  jack_ringbuffer_write( data->buff, ( const char * ) message, nBytes );
 }
 
 #endif  // __UNIX_JACK__
+
+//*********************************************************************//
+//  API: Web MIDI
+//
+//  Written primarily by Atsushi Eno, February 2020.
+//
+//  *********************************************************************//
+
+#if defined(__WEB_MIDI_API__)
+
+#include <emscripten.h>
+
+//*********************************************************************//
+//  API: WEB MIDI
+//  Class Definitions: WebMidiAccessShim
+//*********************************************************************//
+
+class WebMidiAccessShim
+{
+public:
+  WebMidiAccessShim();
+  ~WebMidiAccessShim();
+  std::string getPortName( unsigned int portNumber, bool isInput );
+};
+
+std::unique_ptr<WebMidiAccessShim> shim{nullptr};
+
+void ensureShim()
+{
+  if ( shim.get() != nullptr )
+    return;
+  shim.reset( new WebMidiAccessShim() );
+}
+
+bool checkWebMidiAvailability()
+{
+  ensureShim();
+
+  return MAIN_THREAD_EM_ASM_INT( {
+    if ( typeof window._rtmidi_internals_waiting === "undefined" ) {
+      console.log ( "Attempted to use Web MIDI API without trying to open it." );
+      return false;
+    }
+    if ( window._rtmidi_internals_waiting ) {
+      console.log ( "Attempted to use Web MIDI API while it is being queried." );
+      return false;
+    }
+    if ( _rtmidi_internals_midi_access == null ) {
+      console.log ( "Attempted to use Web MIDI API while it already turned out to be unavailable." );
+      return false;
+    }
+    return true;
+  } );
+}
+
+WebMidiAccessShim::WebMidiAccessShim()
+{
+  MAIN_THREAD_ASYNC_EM_ASM( {
+    if( typeof window._rtmidi_internals_midi_access !== "undefined" )
+      return;
+    if( typeof window._rtmidi_internals_waiting !== "undefined" ) {
+       console.log( "MIDI Access was requested while another request is in progress." );
+       return;
+    }
+
+    // define functions
+    window._rtmidi_internals_get_port_by_number = function( portNumber, isInput ) {
+      var midi = window._rtmidi_internals_midi_access;
+      var devices = isInput ? midi.inputs : midi.outputs;
+      var i = 0;
+      for (var device of devices.values()) {
+        if ( i == portNumber )
+          return device;
+        i++;
+      }
+      console.log( "MIDI " + (isInput ? "input" : "output") + " device of portNumber " + portNumber + " is not found.");
+      return null;
+    };
+
+    window._rtmidi_internals_waiting = true;
+    window.navigator.requestMIDIAccess( {"sysex": true} ).then( (midiAccess) => {
+      window._rtmidi_internals_midi_access = midiAccess;
+      window._rtmidi_internals_latest_message_timestamp = 0.0;
+      window._rtmidi_internals_waiting = false;
+      if( midiAccess == null ) {
+        console.log ( "Could not get access to MIDI API" );
+      }
+    } );
+  } );
+}
+
+WebMidiAccessShim::~WebMidiAccessShim()
+{
+}
+
+std::string WebMidiAccessShim::getPortName( unsigned int portNumber, bool isInput )
+{
+  if( !checkWebMidiAvailability() )
+    return "";
+  char *ret = (char*) MAIN_THREAD_EM_ASM_INT( {
+    var port = window._rtmidi_internals_get_port_by_number($0, $1);
+    if( port == null)
+      return null;
+    var length = lengthBytesUTF8(port.name) + 1;
+    var ret = _malloc(length);
+    stringToUTF8(port.name, ret, length);
+    return ret;
+  }, portNumber, isInput, &ret );
+  if (ret == nullptr)
+      return "";
+  std::string s = ret;
+  free(ret);
+  return s;
+}
+
+//*********************************************************************//
+//  API: WEB MIDI
+//  Class Definitions: MidiInWeb
+//*********************************************************************//
+
+MidiInWeb::MidiInWeb( const std::string &clientName, unsigned int queueSizeLimit )
+  : MidiInApi( queueSizeLimit )
+{
+  initialize( clientName );
+}
+
+MidiInWeb::~MidiInWeb( void )
+{
+  closePort();
+}
+
+extern "C" void EMSCRIPTEN_KEEPALIVE rtmidi_onMidiMessageProc( MidiInApi::RtMidiInData* data, uint8_t* inputBytes, int32_t length, double domHighResTimeStamp )
+{
+  auto &message = data->message;
+  message.bytes.resize(message.bytes.size() + length);
+  memcpy(message.bytes.data(), inputBytes, length);
+  // FIXME: handle timestamp
+  if ( data->usingCallback ) {
+    RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
+    callback( message.timeStamp, &message.bytes, data->userData );
+  }
+}
+
+void MidiInWeb::openPort( unsigned int portNumber, const std::string &portName )
+{
+  if( !checkWebMidiAvailability() )
+    return;
+  if (open_port_number >= 0)
+    return;
+
+  MAIN_THREAD_EM_ASM( {
+    // In Web MIDI API world, there is no step to open a port, but we have to register the input callback instead.
+    var input = window._rtmidi_internals_get_port_by_number($0, true);
+    input.onmidimessage = function(e) {
+      // In RtMidi world, timestamps are delta time from previous message, while in Web MIDI world
+      // timestamps are relative to window creation time (i.e. kind of absolute time with window "epoch" time).
+      var rtmidiTimestamp = window._rtmidi_internals_latest_message_timestamp == 0.0 ? 0.0 : e.timeStamp - window._rtmidi_internals_latest_message_timestamp;
+      window._rtmidi_internals_latest_message_timestamp = e.timeStamp;
+      Module.ccall( 'rtmidi_onMidiMessageProc', 'void', ['number', 'array', 'number', 'number'], [$1, e.data, e.data.length, rtmidiTimestamp] );
+    };
+  }, portNumber, &inputData_ );
+  open_port_number = portNumber;
+}
+
+void MidiInWeb::openVirtualPort( const std::string &portName )
+{
+
+  errorString_ = "MidiInWeb::openVirtualPort: this function is not implemented for the Web MIDI API!";
+  error( RtMidiError::WARNING, errorString_ );
+
+}
+
+void MidiInWeb::closePort( void )
+{
+  if( open_port_number < 0 )
+    return;
+
+  MAIN_THREAD_EM_ASM( {
+    var input = _rtmidi_internals_get_port_by_number($0, true);
+    if( input == null ) {
+      console.log( "Port #" + $0 + " could not be found.");
+      return;
+    }
+    // unregister event handler
+    input.onmidimessage = null;
+  }, open_port_number );
+  open_port_number = -1;
+}
+
+void MidiInWeb::setClientName( const std::string &clientName )
+{
+  client_name = clientName;
+}
+
+void MidiInWeb::setPortName( const std::string &portName )
+{
+
+  errorString_ = "MidiInWeb::setPortName: this function is not implemented for the Web MIDI API!";
+  error( RtMidiError::WARNING, errorString_ );
+
+}
+
+unsigned int MidiInWeb::getPortCount( void )
+{
+  if( !checkWebMidiAvailability() )
+    return 0;
+  return MAIN_THREAD_EM_ASM_INT( { return _rtmidi_internals_midi_access.inputs.size; } );
+}
+
+std::string MidiInWeb::getPortName( unsigned int portNumber )
+{
+  if( !checkWebMidiAvailability() )
+    return "";
+  return shim->getPortName( portNumber, true );
+}
+
+void MidiInWeb::initialize( const std::string& clientName )
+{
+  ensureShim();
+  setClientName( clientName );
+}
+
+//*********************************************************************//
+//  API: WEB MIDI
+//  Class Definitions: MidiOutWeb
+//*********************************************************************//
+
+MidiOutWeb::MidiOutWeb( const std::string &clientName )
+{
+  initialize( clientName );
+}
+
+MidiOutWeb::~MidiOutWeb( void )
+{
+  closePort();
+}
+
+void MidiOutWeb::openPort( unsigned int portNumber, const std::string &portName )
+{
+  if( !checkWebMidiAvailability() )
+    return;
+  if (open_port_number >= 0)
+    return;
+  // In Web MIDI API world, there is no step to open a port.
+
+  open_port_number = portNumber;
+}
+
+void MidiOutWeb::openVirtualPort( const std::string &portName )
+{
+
+  errorString_ = "MidiOutWeb::openVirtualPort: this function is not implemented for the Web MIDI API!";
+  error( RtMidiError::WARNING, errorString_ );
+
+}
+
+void MidiOutWeb::closePort( void )
+{
+  // there is really nothing to do for output at JS side.
+  open_port_number = -1;
+}
+
+void MidiOutWeb::setClientName( const std::string &clientName )
+{
+  client_name = clientName;
+}
+
+void MidiOutWeb::setPortName( const std::string &portName )
+{
+
+  errorString_ = "MidiOutWeb::setPortName: this function is not implemented for the Web MIDI API!";
+  error( RtMidiError::WARNING, errorString_ );
+
+}
+
+unsigned int MidiOutWeb::getPortCount( void )
+{
+  if( !checkWebMidiAvailability() )
+    return 0;
+  return MAIN_THREAD_EM_ASM_INT( { return _rtmidi_internals_midi_access.outputs.size; } );
+}
+
+std::string MidiOutWeb::getPortName( unsigned int portNumber )
+{
+  if( !checkWebMidiAvailability() )
+    return "";
+  return shim->getPortName( portNumber, false );
+}
+
+void MidiOutWeb::sendMessage( const unsigned char *message, size_t size )
+{
+  if( open_port_number < 0 )
+    return;
+
+  MAIN_THREAD_EM_ASM( {
+    var output = _rtmidi_internals_get_port_by_number( $0, false );
+    if( output == null ) {
+      console.log( "Port #" + $0 + " could not be found.");
+      return;
+    }
+    var buf = new ArrayBuffer ($2);
+    var msg = new Uint8Array( buf );
+    msg.set( new Uint8Array( Module.HEAPU8.buffer.slice( $1, $1 + $2 ) ) );
+    output.send( msg );
+  }, open_port_number, message, size );
+}
+
+void MidiOutWeb::initialize( const std::string& clientName )
+{
+  if ( shim.get() != nullptr )
+    return;
+  shim.reset( new WebMidiAccessShim() );
+  setClientName( clientName );
+}
+
+#endif  // __WEB_MIDI_API__
diff --git a/rtmidi/RtMidi.h b/rtmidi/RtMidi.h
--- a/rtmidi/RtMidi.h
+++ b/rtmidi/RtMidi.h
@@ -9,7 +9,7 @@
     RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/
 
     RtMidi: realtime MIDI i/o C++ classes
-    Copyright (c) 2003-2019 Gary P. Scavone
+    Copyright (c) 2003-2021 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -58,13 +58,14 @@
   #endif
 #endif
 
-#define RTMIDI_VERSION "4.0.0"
+#define RTMIDI_VERSION "5.0.0"
 
 #include <exception>
 #include <iostream>
 #include <string>
 #include <vector>
 
+
 /************************************************************************/
 /*! \class RtMidiError
     \brief Exception handling class for RtMidi.
@@ -132,6 +133,8 @@
 class RTMIDI_DLL_PUBLIC RtMidi
 {
  public:
+
+     RtMidi(RtMidi&& other) noexcept;
   //! MIDI API specifier arguments.
   enum Api {
     UNSPECIFIED,    /*!< Search for a working compiled API. */
@@ -140,6 +143,7 @@
     UNIX_JACK,      /*!< The JACK Low-Latency MIDI Server API. */
     WINDOWS_MM,     /*!< The Microsoft Multimedia MIDI API. */
     RTMIDI_DUMMY,   /*!< A compilable but non-functional API. */
+    WEB_MIDI_API,   /*!< W3C Web MIDI API. */
     NUM_APIS        /*!< Number of values in this enum. */
   };
 
@@ -213,6 +217,10 @@
   RtMidi();
   virtual ~RtMidi();
   MidiApi *rtapi_;
+
+  /* Make the class non-copyable */
+  RtMidi(RtMidi& other) = delete;
+  RtMidi& operator=(RtMidi& other) = delete;
 };
 
 /**********************************************************************/
@@ -228,8 +236,6 @@
     time.  With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also
     possible to open a virtual input port to which other MIDI software
     clients can connect.
-
-    by Gary P. Scavone, 2003-2017.
 */
 /**********************************************************************/
 
@@ -250,7 +256,6 @@
 class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi
 {
  public:
-
   //! User callback function type definition.
   typedef void (*RtMidiCallback)( double timeStamp, std::vector<unsigned char> *message, void *userData );
 
@@ -276,6 +281,8 @@
             const std::string& clientName = "RtMidi Input Client",
             unsigned int queueSizeLimit = 100 );
 
+  RtMidiIn(RtMidiIn&& other) noexcept : RtMidi(std::move(other)) { }
+
   //! If a MIDI connection is still open, it will be closed by the destructor.
   ~RtMidiIn ( void ) throw();
 
@@ -373,6 +380,19 @@
   */
   virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );
 
+  //! Set maximum expected incoming message size.
+  /*!
+    For APIs that require manual buffer management, it can be useful to set the buffer
+    size and buffer count when expecting to receive large SysEx messages.  Note that
+    currently this function has no effect when called after openPort().  The default
+    buffer size is 1024 with a count of 4 buffers, which should be sufficient for most
+    cases; as mentioned, this does not affect all API backends, since most either support
+    dynamically scalable buffers or take care of buffer handling themselves.  It is
+    principally intended for users of the Windows MM backend who must support receiving
+    especially large messages.
+  */
+  virtual void setBufferSize( unsigned int size, unsigned int count );
+
  protected:
   void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit );
 };
@@ -388,8 +408,6 @@
     connect to more than one MIDI device at the same time.  With the
     OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a
     virtual port to which other MIDI software clients can connect.
-
-    by Gary P. Scavone, 2003-2017.
 */
 /**********************************************************************/
 
@@ -407,6 +425,8 @@
   RtMidiOut( RtMidi::Api api=UNSPECIFIED,
              const std::string& clientName = "RtMidi Output Client" );
 
+  RtMidiOut(RtMidiOut&& other) noexcept : RtMidi(std::move(other)) { }
+
   //! The destructor closes any open MIDI connections.
   ~RtMidiOut( void ) throw();
 
@@ -527,6 +547,7 @@
   RtMidiErrorCallback errorCallback_;
   bool firstErrorOccurred_;
   void *errorCallbackUserData_;
+
 };
 
 class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
@@ -539,6 +560,7 @@
   void cancelCallback( void );
   virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense );
   double getMessage( std::vector<unsigned char> *message );
+  virtual void setBufferSize( unsigned int size, unsigned int count );
 
   // A MIDI structure used internally by the class to store incoming
   // messages.  Each message represents one and only one MIDI message.
@@ -580,11 +602,13 @@
     RtMidiIn::RtMidiCallback userCallback;
     void *userData;
     bool continueSysex;
+    unsigned int bufferSize;
+    unsigned int bufferCount;
 
     // Default constructor.
     RtMidiInData()
       : ignoreFlags(7), doInput(false), firstMessage(true), apiData(0), usingCallback(false),
-        userCallback(0), userData(0), continueSysex(false) {}
+        userCallback(0), userData(0), continueSysex(false), bufferSize(1024), bufferCount(4) {}
   };
 
  protected:
@@ -618,6 +642,7 @@
 inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { static_cast<MidiInApi *>(rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); }
 inline double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) { return static_cast<MidiInApi *>(rtapi_)->getMessage( message ); }
 inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); }
+inline void RtMidiIn :: setBufferSize( unsigned int size, unsigned int count ) { static_cast<MidiInApi *>(rtapi_)->setBufferSize(size, count); }
 
 inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
 inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }
diff --git a/rtmidi/rtmidi_c.cpp b/rtmidi/rtmidi_c.cpp
--- a/rtmidi/rtmidi_c.cpp
+++ b/rtmidi/rtmidi_c.cpp
@@ -17,6 +17,7 @@
     ENUM_EQUAL( RTMIDI_API_UNIX_JACK,       RtMidi::UNIX_JACK );
     ENUM_EQUAL( RTMIDI_API_WINDOWS_MM,      RtMidi::WINDOWS_MM );
     ENUM_EQUAL( RTMIDI_API_RTMIDI_DUMMY,    RtMidi::RTMIDI_DUMMY );
+    ENUM_EQUAL( RTMIDI_API_WEB_MIDI_API,    RtMidi::WEB_MIDI_API );
 
     ENUM_EQUAL( RTMIDI_ERROR_WARNING,            RtMidiError::WARNING );
     ENUM_EQUAL( RTMIDI_ERROR_DEBUG_WARNING,      RtMidiError::DEBUG_WARNING );
@@ -34,12 +35,12 @@
 class CallbackProxyUserData
 {
   public:
-	CallbackProxyUserData (RtMidiCCallback cCallback, void *userData)
-		: c_callback (cCallback), user_data (userData)
-	{
-	}
-	RtMidiCCallback c_callback;
-	void *user_data;
+  CallbackProxyUserData (RtMidiCCallback cCallback, void *userData)
+    : c_callback (cCallback), user_data (userData)
+  {
+  }
+  RtMidiCCallback c_callback;
+  void *user_data;
 };
 
 extern "C" const enum RtMidiApi rtmidi_compiled_apis[]; // casting from RtMidi::Api[]
@@ -80,8 +81,8 @@
 
 void rtmidi_error (MidiApi *api, enum RtMidiErrorType type, const char* errorString)
 {
-	std::string msg = errorString;
-	api->error ((RtMidiError::Type) type, msg);
+  std::string msg = errorString;
+  api->error ((RtMidiError::Type) type, msg);
 }
 
 void rtmidi_open_port (RtMidiPtr device, unsigned int portNumber, const char *portName)
@@ -89,7 +90,7 @@
     std::string name = portName;
     try {
         ((RtMidi*) device->ptr)->openPort (portNumber, name);
-    
+
     } catch (const RtMidiError & err) {
         device->ok  = false;
         device->msg = err.what ();
@@ -101,7 +102,7 @@
     std::string name = portName;
     try {
         ((RtMidi*) device->ptr)->openVirtualPort (name);
-    
+
     } catch (const RtMidiError & err) {
         device->ok  = false;
         device->msg = err.what ();
@@ -111,7 +112,7 @@
 
 void rtmidi_close_port (RtMidiPtr device)
 {
-    try { 
+    try {
         ((RtMidi*) device->ptr)->closePort ();
 
     } catch (const RtMidiError & err) {
@@ -132,32 +133,42 @@
     }
 }
 
-const char* rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber)
+int rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber, char * bufOut, int * bufLen)
 {
+    if (bufOut == nullptr && bufLen == nullptr) {
+        return -1;
+    }
+
+    std::string name;
     try {
-        std::string name = ((RtMidi*) device->ptr)->getPortName (portNumber);
-        return strdup (name.c_str ());
-    
+        name = ((RtMidi*) device->ptr)->getPortName (portNumber);
     } catch (const RtMidiError & err) {
         device->ok  = false;
         device->msg = err.what ();
-        return "";
+        return -1;
     }
+
+    if (bufOut == nullptr) {
+        *bufLen = static_cast<int>(name.size()) + 1;
+        return 0;
+    }
+
+    return snprintf(bufOut, static_cast<size_t>(*bufLen), "%s", name.c_str());
 }
 
 /* RtMidiIn API */
 RtMidiInPtr rtmidi_in_create_default ()
 {
     RtMidiWrapper* wrp = new RtMidiWrapper;
-    
+
     try {
         RtMidiIn* rIn = new RtMidiIn ();
-        
+
         wrp->ptr = (void*) rIn;
         wrp->data = 0;
         wrp->ok  = true;
         wrp->msg = "";
-    
+
     } catch (const RtMidiError & err) {
         wrp->ptr = 0;
         wrp->data = 0;
@@ -172,10 +183,10 @@
 {
     std::string name = clientName;
     RtMidiWrapper* wrp = new RtMidiWrapper;
-    
+
     try {
         RtMidiIn* rIn = new RtMidiIn ((RtMidi::Api) api, name, queueSizeLimit);
-        
+
         wrp->ptr = (void*) rIn;
         wrp->data = 0;
         wrp->ok  = true;
@@ -203,7 +214,7 @@
 {
     try {
         return (RtMidiApi) ((RtMidiIn*) device->ptr)->getCurrentApi ();
-    
+
     } catch (const RtMidiError & err) {
         device->ok  = false;
         device->msg = err.what ();
@@ -215,8 +226,8 @@
 static
 void callback_proxy (double timeStamp, std::vector<unsigned char> *message, void *userData)
 {
-	CallbackProxyUserData* data = reinterpret_cast<CallbackProxyUserData*> (userData);
-	data->c_callback (timeStamp, message->data (), message->size (), data->user_data);
+  CallbackProxyUserData* data = reinterpret_cast<CallbackProxyUserData*> (userData);
+  data->c_callback (timeStamp, message->data (), message->size (), data->user_data);
 }
 
 void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void *userData)
@@ -246,10 +257,10 @@
 
 void rtmidi_in_ignore_types (RtMidiInPtr device, bool midiSysex, bool midiTime, bool midiSense)
 {
-	((RtMidiIn*) device->ptr)->ignoreTypes (midiSysex, midiTime, midiSense);
+  ((RtMidiIn*) device->ptr)->ignoreTypes (midiSysex, midiTime, midiSense);
 }
 
-double rtmidi_in_get_message (RtMidiInPtr device, 
+double rtmidi_in_get_message (RtMidiInPtr device,
                               unsigned char *message,
                               size_t *size)
 {
@@ -264,7 +275,7 @@
 
         *size = v.size();
         return ret;
-    } 
+    }
     catch (const RtMidiError & err) {
         device->ok  = false;
         device->msg = err.what ();
@@ -284,12 +295,12 @@
 
     try {
         RtMidiOut* rOut = new RtMidiOut ();
-        
+
         wrp->ptr = (void*) rOut;
         wrp->data = 0;
         wrp->ok  = true;
         wrp->msg = "";
-    
+
     } catch (const RtMidiError & err) {
         wrp->ptr = 0;
         wrp->data = 0;
@@ -307,12 +318,12 @@
 
     try {
         RtMidiOut* rOut = new RtMidiOut ((RtMidi::Api) api, name);
-        
+
         wrp->ptr = (void*) rOut;
         wrp->data = 0;
         wrp->ok  = true;
         wrp->msg = "";
-    
+
     } catch (const RtMidiError & err) {
         wrp->ptr = 0;
         wrp->data = 0;
diff --git a/rtmidi/rtmidi_c.h b/rtmidi/rtmidi_c.h
--- a/rtmidi/rtmidi_c.h
+++ b/rtmidi/rtmidi_c.h
@@ -41,7 +41,7 @@
     void* ptr;
     void* data;
 
-    //! True when the last function call was OK. 
+    //! True when the last function call was OK.
     bool  ok;
 
     //! If an error occured (ok != true), set to an error message.
@@ -65,6 +65,7 @@
     RTMIDI_API_UNIX_JACK,      /*!< The Jack Low-Latency MIDI Server API. */
     RTMIDI_API_WINDOWS_MM,     /*!< The Microsoft Multimedia MIDI API. */
     RTMIDI_API_RTMIDI_DUMMY,   /*!< A compilable but non-functional API. */
+    RTMIDI_API_WEB_MIDI_API,   /*!< W3C Web MIDI API. */
     RTMIDI_API_NUM             /*!< Number of values in this enum. */
 };
 
@@ -136,8 +137,8 @@
  */
 RTMIDIAPI void rtmidi_open_port (RtMidiPtr device, unsigned int portNumber, const char *portName);
 
-/*! \brief Creates a virtual MIDI port to which other software applications can 
- * connect.  
+/*! \brief Creates a virtual MIDI port to which other software applications can
+ * connect.
  *
  * \param portName  Name for the application port.
  *
@@ -155,10 +156,14 @@
  */
 RTMIDIAPI unsigned int rtmidi_get_port_count (RtMidiPtr device);
 
-/*! \brief Return a string identifier for the specified MIDI input port number.
+/*! \brief Access a string identifier for the specified MIDI input port number.
+ *
+ * To prevent memory leaks a char buffer must be passed to this function.
+ * NULL can be passed as bufOut parameter, and that will write the required buffer length in the bufLen.
+ *
  * See RtMidi::getPortName().
  */
-RTMIDIAPI const char* rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber);
+RTMIDIAPI int rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber, char * bufOut, int * bufLen);
 
 /* RtMidiIn API */
 
@@ -203,8 +208,9 @@
  * \param message   Must point to a char* that is already allocated.
  *                  SYSEX messages maximum size being 1024, a statically
  *                  allocated array could
- *                  be sufficient. 
- * \param size      Is used to return the size of the message obtained. 
+ *                  be sufficient.
+ * \param size      Is used to return the size of the message obtained.
+ *                  Must be set to the size of \ref message when calling.
  *
  * See RtMidiIn::getMessage().
  */
