packages feed

RtMidi 0.1.1.0 → 0.2.0.0

raw patch · 13 files changed

+4431/−3485 lines, 13 filesdep +RtMididep +tastydep +tasty-hunitdep ~basenew-component:exe:rtmidi-callbacknew-component:exe:rtmidi-playbacknew-component:exe:rtmidi-pollnew-component:exe:rtmidi-querynew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: RtMidi, tasty, tasty-hunit, unliftio-core

Dependency ranges changed: base

API changes (from Hackage documentation)

- Sound.RtMidi: DebugWarning :: ErrorType
- Sound.RtMidi: DriverError :: ErrorType
- Sound.RtMidi: InvalidDevice :: ErrorType
- Sound.RtMidi: InvalidParameter :: ErrorType
- Sound.RtMidi: InvalidUse :: ErrorType
- Sound.RtMidi: MemoryError :: ErrorType
- Sound.RtMidi: NoDevicesFound :: ErrorType
- Sound.RtMidi: SystemError :: ErrorType
- Sound.RtMidi: ThreadError :: ErrorType
- Sound.RtMidi: UnspecifiedError :: ErrorType
- Sound.RtMidi: Warning :: ErrorType
- Sound.RtMidi: closeInput :: Device -> IO ()
- Sound.RtMidi: closeOutput :: Device -> IO ()
- Sound.RtMidi: data Device
- Sound.RtMidi: data Error
- Sound.RtMidi: data ErrorType
- Sound.RtMidi: instance Foreign.Storable.Storable Sound.RtMidi.Wrapper
- Sound.RtMidi: instance GHC.Classes.Eq Sound.RtMidi.ErrorType
- Sound.RtMidi: instance GHC.Classes.Eq Sound.RtMidi.Wrapper
- Sound.RtMidi: instance GHC.Enum.Enum Sound.RtMidi.ErrorType
- Sound.RtMidi: instance GHC.Show.Show Sound.RtMidi.ErrorType
- Sound.RtMidi: instance GHC.Show.Show Sound.RtMidi.Wrapper
- Sound.RtMidi: reportError :: Device -> ErrorType -> String -> IO ()
- Sound.RtMidi: setCallbackWithUserData :: Device -> (CDouble -> [CUChar] -> Ptr () -> IO ()) -> Ptr () -> IO ()
+ Sound.RtMidi: InputDeviceType :: DeviceType
+ Sound.RtMidi: OutputDeviceType :: DeviceType
+ Sound.RtMidi: class IsDevice d
+ Sound.RtMidi: closeDevice :: MonadIO m => IsDevice d => d -> m ()
+ Sound.RtMidi: data DeviceType
+ Sound.RtMidi: data InputDevice
+ Sound.RtMidi: data OutputDevice
+ Sound.RtMidi: findPort :: (MonadIO m, IsDevice d) => d -> (String -> Bool) -> m (Maybe Int)
+ Sound.RtMidi: getDeviceType :: IsDevice d => d -> DeviceType
+ Sound.RtMidi: getMessageSized :: MonadIO m => InputDevice -> Int -> m (Double, [Word8])
+ Sound.RtMidi: instance GHC.Classes.Eq Sound.RtMidi.Device
+ Sound.RtMidi: instance GHC.Classes.Eq Sound.RtMidi.DeviceType
+ Sound.RtMidi: instance GHC.Classes.Eq Sound.RtMidi.Error
+ Sound.RtMidi: instance GHC.Classes.Eq Sound.RtMidi.InputDevice
+ Sound.RtMidi: instance GHC.Classes.Eq Sound.RtMidi.OutputDevice
+ Sound.RtMidi: instance GHC.Exception.Type.Exception Sound.RtMidi.Error
+ Sound.RtMidi: instance GHC.Show.Show Sound.RtMidi.Device
+ Sound.RtMidi: instance GHC.Show.Show Sound.RtMidi.DeviceType
+ Sound.RtMidi: instance GHC.Show.Show Sound.RtMidi.Error
+ Sound.RtMidi: instance GHC.Show.Show Sound.RtMidi.InputDevice
+ Sound.RtMidi: instance GHC.Show.Show Sound.RtMidi.OutputDevice
+ Sound.RtMidi: instance Sound.RtMidi.IsDevice Sound.RtMidi.InputDevice
+ Sound.RtMidi: instance Sound.RtMidi.IsDevice Sound.RtMidi.OutputDevice
+ Sound.RtMidi: listPorts :: (MonadIO m, IsDevice d) => d -> m [(Int, String)]
+ Sound.RtMidi: newtype Error
- Sound.RtMidi: Error :: ErrorType -> String -> Error
+ Sound.RtMidi: Error :: String -> Error
- Sound.RtMidi: cancelCallback :: Device -> IO ()
+ Sound.RtMidi: cancelCallback :: MonadIO m => InputDevice -> m ()
- Sound.RtMidi: closePort :: Device -> IO ()
+ Sound.RtMidi: closePort :: (MonadIO m, IsDevice d) => d -> m ()
- Sound.RtMidi: compiledApis :: IO [Api]
+ Sound.RtMidi: compiledApis :: MonadIO m => m [Api]
- Sound.RtMidi: createInput :: Api -> String -> Int -> IO Device
+ Sound.RtMidi: createInput :: MonadIO m => Api -> String -> Int -> m InputDevice
- Sound.RtMidi: createOutput :: Api -> String -> IO Device
+ Sound.RtMidi: createOutput :: MonadIO m => Api -> String -> m OutputDevice
- Sound.RtMidi: currentApi :: Device -> IO Api
+ Sound.RtMidi: currentApi :: MonadIO m => IsDevice d => d -> m Api
- Sound.RtMidi: defaultInput :: IO Device
+ Sound.RtMidi: defaultInput :: MonadIO m => m InputDevice
- Sound.RtMidi: defaultOutput :: IO Device
+ Sound.RtMidi: defaultOutput :: MonadIO m => m OutputDevice
- Sound.RtMidi: getMessage :: Device -> IO ([CUChar], Double)
+ Sound.RtMidi: getMessage :: MonadIO m => InputDevice -> m (Double, [Word8])
- Sound.RtMidi: ignoreTypes :: Device -> Bool -> Bool -> Bool -> IO ()
+ Sound.RtMidi: ignoreTypes :: MonadIO m => InputDevice -> Bool -> Bool -> Bool -> m ()
- Sound.RtMidi: openPort :: Device -> Int -> String -> IO ()
+ Sound.RtMidi: openPort :: (MonadIO m, IsDevice d) => d -> Int -> String -> m ()
- Sound.RtMidi: openVirtualPort :: Device -> String -> IO ()
+ Sound.RtMidi: openVirtualPort :: (MonadIO m, IsDevice d) => d -> String -> m ()
- Sound.RtMidi: portCount :: Device -> IO Int
+ Sound.RtMidi: portCount :: (MonadIO m, IsDevice d) => d -> m Int
- Sound.RtMidi: portName :: Device -> Int -> IO String
+ Sound.RtMidi: portName :: (MonadIO m, IsDevice d) => d -> Int -> m (Maybe String)
- Sound.RtMidi: ready :: Device -> IO Bool
+ Sound.RtMidi: ready :: (MonadIO m, IsDevice d) => d -> m Bool
- Sound.RtMidi: sendMessage :: Device -> [CUChar] -> IO ()
+ Sound.RtMidi: sendMessage :: MonadIO m => OutputDevice -> [Word8] -> m ()
- Sound.RtMidi: setCallback :: Device -> (CDouble -> [CUChar] -> IO ()) -> IO ()
+ Sound.RtMidi: setCallback :: MonadUnliftIO m => InputDevice -> (Double -> [Word8] -> m ()) -> m ()

Files

+ README.md view
@@ -0,0 +1,35 @@+# RtMidi++Haskell wrapper for [RtMidi](http://www.music.mcgill.ca/~gary/rtmidi/), the lightweight, cross-platform MIDI I/O library.++## Development++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:++    # Build on OSX CoreMIDI support or Linux with ALSA support+    # You can also pass `--flag RtMidi:jack` for Jack support+    stack build++    # Verify that it works:+    stack exec -- rtmidi-query++There is also a `Dockerfile` in the `docker` directory and some `make` targets that can help you verify Linux builds:++    # Build the image and tag it `haskell-rtmidi-dev`+    make docker-build++    # Enter the docker image+    make docker-repl++    # Inside the docker image:+    cabal update && cabal build -fjack++(Note that you can't use any `RtMidi` functions in the containerized env unless you are running a Linux host, and+even then you'd probably have to start the process with something like `docker run --device /dev/snd`.)++## TODO++* Add Windows MM support. This should only require a few changes to the Cabal file.+* See if there is a way to autodetect Jack in the Cabal file.
RtMidi.cabal view
@@ -1,23 +1,26 @@ name:                RtMidi-version:             0.1.1.0-description:         Haskell wrapper for RtMidi, the lightweight, cross-platform MIDI I/O library.+version:             0.2.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 build-type:          Simple-author:              kohlrabi+author:              kohlrabi, Eric Conlon license-file:        LICENSE homepage:            https://github.com/riottracker/RtMidi-maintainer:          kohlrabi@kohlra.biz-extra-source-files:  rtmidi/rtmidi_c.h+maintainer:          ejconlon@gmail.com+extra-source-files:  README.md+                     rtmidi/rtmidi_c.h                      rtmidi/RtMidi.h+                     examples/callback.hs                      examples/playback.hs+                     examples/poll.hs                      examples/query.hs-                     examples/callback.hs cabal-version:       >=1.10 license:             MIT  Flag alsa {   Description:  Enable ALSA sequencer api-  Default:      False+  Default:      True }  Flag jack {@@ -27,37 +30,91 @@  Flag core {   Description:  Enable CoreMIDI api-  Default:      False+  Default:      True } -Flag mm {-  Description:  Enable Windows Multimedia Library api-  Default:      False-}+-- TODO(ejconlon) Add windows support+-- Flag mm {+--   Description:  Enable Windows Multimedia Library api+--   Default:      False+-- }   library   exposed-modules:     Sound.RtMidi-  -- other-modules:       -  -- other-extensions:    -  build-depends:       base >=4.9 && <4.13+  other-modules:       Sound.RtMidi.Foreign+  build-depends:       base >=4.9 && <4.15+                     , unliftio-core >= 0.1.2.0 && < 1   default-language:    Haskell2010   include-dirs:        rtmidi   extra-libraries:     stdc++   c-sources:           rtmidi/RtMidi.cpp                        rtmidi/rtmidi_c.cpp-  if flag(alsa)+  if flag(alsa) && os(linux)     cc-options:       -D__LINUX_ALSA__     extra-libraries:  asound pthread-  if flag(jack)+  if flag(jack) && (os(linux) || os(darwin))     cc-options:       -D__UNIX_JACK__     extra-libraries:  jack-  if flag(core)-    cc-options:       -D__MAXOSX_CORE__-    extra-libraries:  CoreMIDI CoreAudio CoreFoundation+  if flag(core) && os(darwin)+    cc-options:       -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.+    -- Cribbed from https://github.com/fpco/inline-c/pull/89+    -- avoid https://gitlab.haskell.org/ghc/ghc/issues/11829+    ld-options:        -Wl,-keep_dwarf_unwind+    ghc-options:       -pgmc=clang+++  -- TODO(ejconlon) Add windows support +executable rtmidi-callback+  main-is:            callback.hs+  hs-source-dirs:     examples+  build-depends:+      base+    , RtMidi+  default-language:   Haskell2010+  ghc-options: -threaded -rtsopts +executable rtmidi-playback+  main-is:            playback.hs+  hs-source-dirs:     examples+  build-depends:+      base+    , RtMidi+  default-language:   Haskell2010+  ghc-options: -threaded -rtsopts++executable rtmidi-poll+  main-is:            poll.hs+  hs-source-dirs:     examples+  build-depends:+      base+    , RtMidi+  default-language:   Haskell2010+  ghc-options: -threaded -rtsopts++executable rtmidi-query+  main-is:            query.hs+  hs-source-dirs:     examples+  build-depends:+      base+    , RtMidi+  default-language:   Haskell2010+  ghc-options: -threaded -rtsopts++test-suite rtmidi-test+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs: test+  build-depends:+      base+    , RtMidi+    , tasty >= 1.2.3 && < 2+    , tasty-hunit >= 0.10.0.2 && < 1+  default-language:   Haskell2010+  ghc-options: -threaded -rtsopts+ source-repository head   type:     git   location: https://github.com/riottracker/RtMidi-
Sound/RtMidi.hs view
@@ -1,54 +1,84 @@-{-# LANGUAGE ForeignFunctionInterface #-}+-- | Interface to RtMidi+module Sound.RtMidi+  ( InputDevice+  , OutputDevice+  , IsDevice (getDeviceType)+  , DeviceType (..)+  , Api (..)+  , Error (..)+  , ready+  , compiledApis+  , openPort+  , openVirtualPort+  , closePort+  , portCount+  , portName+  , listPorts+  , findPort+  , defaultInput+  , createInput+  , setCallback+  , cancelCallback+  , ignoreTypes+  , getMessage+  , getMessageSized+  , defaultOutput+  , createOutput+  , sendMessage+  , closeDevice+  , currentApi+  ) where +import Control.Exception (Exception, throwIO)+import Control.Monad (unless)+import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.IO.Unlift (MonadUnliftIO (..))+import Data.Coerce (coerce)+import Data.Word (Word8)+import Foreign (FunPtr, Ptr, Storable (..), alloca, allocaArray, nullPtr, peekArray, with, withArrayLen)+import Foreign.C (CDouble (..), CInt (..), CSize, CString, CUChar (..), peekCString, withCString)+import Sound.RtMidi.Foreign --- | Interface to RtMidi-module Sound.RtMidi (-      Device-    , Error (..)-    , ErrorType (..)-    , Api(..)-    , ready-    , reportError---    , checkForErrors-    , compiledApis-    , openPort-    , openVirtualPort-    , closePort-    , portCount-    , portName-    , defaultInput-    , createInput-    , setCallback-    , setCallbackWithUserData-    , cancelCallback-    , ignoreTypes-    , getMessage-    , defaultOutput-    , createOutput-    , sendMessage-    , closeInput-    , closeOutput-    , currentApi-    ) where+-- The default message size (in bytes) expected from 'getMessage'+-- Mostly just needs to be bigger than the max MIDI message size, which is 3 bytes+-- However, sysex messages can be quite large, so you might have to use the 'getMessageSized' variant.+defaultMessageSize :: Int+defaultMessageSize = 4 -import Control.Monad-import Foreign-import Foreign.C-import Foreign.C.String+-- | Allows us to discriminate in/out functions in generic contexts+data DeviceType =+    InputDeviceType+  | OutputDeviceType+  deriving (Eq, Show) +newtype Device = Device { unDevice :: Ptr Wrapper }+  deriving (Eq, Show) -data Device = Input (Ptr Wrapper) | Output (Ptr Wrapper)+-- | Generalizes 'InputDevice' and 'OutputDevice' for use in common functions+class IsDevice d where+  toDevice :: d -> Device+  getDeviceType :: d -> DeviceType -device :: Device -> Ptr Wrapper-device (Input x) = x-device (Output x) = x+toDevicePtr :: IsDevice d => d -> Ptr Wrapper+toDevicePtr = unDevice . toDevice -toInput :: Device -> Ptr Wrapper-toInput (Input x) = x+-- | A handle to a device to be used for input+newtype InputDevice = InputDevice { unInputDevice :: Device }+  deriving (Eq, Show) -toOutput :: Device -> Ptr Wrapper-toOutput (Output x) = x+instance IsDevice InputDevice where+  toDevice = unInputDevice+  getDeviceType _ = InputDeviceType +-- | A handle to a device to be used for input+newtype OutputDevice = OutputDevice { unOutputDevice :: Device }+  deriving (Eq, Show)++instance IsDevice OutputDevice where+  toDevice = unOutputDevice+  getDeviceType _ = OutputDeviceType++-- | Enum of RtMidi-supported APIs data Api   = UnspecifiedApi   | CoreMidiApi@@ -73,279 +103,243 @@   toEnum 4 = MultimediaApi   toEnum 5 = DummyApi -data ErrorType-  = Warning-  | DebugWarning-  | UnspecifiedError-  | NoDevicesFound-  | InvalidDevice-  | MemoryError-  | InvalidParameter-  | InvalidUse-  | DriverError-  | SystemError-  | ThreadError-  deriving (Eq, Show)--instance Enum ErrorType where-  fromEnum Warning = 0-  fromEnum DebugWarning = 1-  fromEnum UnspecifiedError = 2-  fromEnum NoDevicesFound = 3-  fromEnum InvalidDevice = 4-  fromEnum MemoryError = 5-  fromEnum InvalidParameter = 6-  fromEnum InvalidUse = 7-  fromEnum DriverError = 8-  fromEnum SystemError = 9-  fromEnum ThreadError = 10-  toEnum 0 = Warning-  toEnum 1 = DebugWarning-  toEnum 2 = UnspecifiedError-  toEnum 3 = NoDevicesFound-  toEnum 4 = InvalidDevice-  toEnum 5 = MemoryError-  toEnum 6 = InvalidParameter-  toEnum 7 = InvalidUse-  toEnum 8 = DriverError-  toEnum 9 = SystemError-  toEnum 10 = ThreadError---data Error = Error ErrorType String--data Wrapper = Wrapper-             { ptr :: Ptr ()-             , ok  :: Bool-             , msg :: CString-             } deriving (Show, Eq)--instance Storable Wrapper where-   sizeOf _  = 24-   alignment = sizeOf-   peek ptr  = do-      a <- peekByteOff ptr 0-      b <- peekByteOff ptr 8-      c <- peekByteOff ptr 16-      return $ Wrapper a b c- -- | Check if a device is ok-ready :: Device -> IO Bool-ready d = fmap ok $ peek (device d)---checkForErrors :: Device -> IO [Char]-checkForErrors d = peek (device d) >>= (\w -> do-    (putStrLn $ show w)-    a <- peekArray0 0 $ plusPtr (castPtr (msg w)) 10-    return a) >>= return . map castCCharToChar--reportError :: Device -> ErrorType -> String -> IO ()-reportError d et em = withCString em $ rtmidi_error (device d) (toEnum . fromEnum $ et)--foreign import ccall "rtmidi_c.h rtmidi_error"-   rtmidi_error :: Ptr Wrapper -> CInt -> CString -> IO ()- -foreign import ccall "rtmidi_c.h rtmidi_sizeof_rtmidi_api"-   rtmidi_sizeof_rtmidi_api :: IO CInt---foreign import ccall "rtmidi_c.h rtmidi_get_compiled_api"-   rtmidi_get_compiled_api :: Ptr (Ptr CInt) -> IO CInt---foreign import ccall "rtmidi_c.h rtmidi_open_port"-   rtmidi_open_port :: Ptr Wrapper -> CInt -> CString -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_open_virtual_port"-   rtmidi_open_virtual_port :: Ptr Wrapper -> CString -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_close_port"-   rtmidi_close_port :: Ptr Wrapper -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_get_port_count"-   rtmidi_get_port_count :: Ptr Wrapper -> IO CInt--foreign import ccall "rtmidi_c.h rtmidi_get_port_name"-   rtmidi_get_port_name :: Ptr Wrapper -> CInt -> IO CString---foreign import ccall "rtmidi_c.h rtmidi_in_create_default"-   rtmidi_in_create_default :: IO (Ptr Wrapper)--foreign import ccall "rtmidi_c.h rtmidi_in_create"-   rtmidi_in_create :: CInt -> CString -> CInt -> IO (Ptr Wrapper)--foreign import ccall "rtmidi_c.h rtmidi_in_free"-   rtmidi_in_free :: Ptr Wrapper -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_in_get_current_api"-   rtmidi_in_get_current_api :: Ptr Wrapper -> IO CInt--foreign import ccall "rtmidi_c.h rtmidi_in_set_callback"-   rtmidi_in_set_callback :: Ptr Wrapper -> FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()) -> Ptr () -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_in_cancel_callback"-   rtmidi_in_cancel_callback :: Ptr Wrapper -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_in_ignore_types"-   rtmidi_in_ignore_types :: Ptr Wrapper -> Bool -> Bool -> Bool -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_in_get_message"-   rtmidi_in_get_message :: Ptr Wrapper -> Ptr (Ptr CUChar) -> Ptr CSize -> IO CDouble--foreign import ccall "rtmidi_c.h rtmidi_out_create_default"-   rtmidi_out_create_default :: IO (Ptr Wrapper)--foreign import ccall "rtmidi_c.h rtmidi_out_create"-   rtmidi_out_create :: CInt -> CString -> IO (Ptr Wrapper)--foreign import ccall "rtmidi_c.h rtmidi_out_free"-   rtmidi_out_free :: Ptr Wrapper -> IO ()--foreign import ccall "rtmidi_c.h rtmidi_out_get_current_api"-   rtmidi_out_get_current_api :: Ptr Wrapper -> IO CInt--foreign import ccall "rtmidi_c.h rtmidi_out_send_message"-   rtmidi_out_send_message :: Ptr Wrapper -> Ptr CUChar -> CInt -> IO CInt-+ready :: (MonadIO m, IsDevice d) => d -> m Bool+ready = liftIO . fmap ok . peek  . toDevicePtr -apiSize :: IO Int-apiSize = fromEnum <$> rtmidi_sizeof_rtmidi_api+-- | An internal RtMidi error+newtype Error = Error String deriving (Eq, Show)+instance Exception Error --- |A static function to determine MIDI 'Api's built in.-compiledApis :: IO [Api]-compiledApis = fmap (map (toEnum . fromEnum)) $ do-   n <- fromIntegral <$> rtmidi_get_compiled_api nullPtr-   allocaArray n $ flip with $ \ptr -> do-      rtmidi_get_compiled_api ptr-      peekArray n =<< peek ptr+-- Detects and throws internal errors+guardError :: Ptr Wrapper -> IO ()+guardError dptr = do+  w <- peek dptr+  unless (ok w) $ do+    e <- peekCString (msg w)+    throwIO (Error e) --- -- |Report an error--- reportError :: Error -> IO ()--- reportError (Error e s) = withCString s $ rtmidi_error (toEnum $ fromEnum $ e)+-- | A static function to determine MIDI 'Api's built in.+compiledApis :: MonadIO m => m [Api]+compiledApis = liftIO $ do+  n <- fmap fromIntegral (rtmidi_get_compiled_api nullPtr)+  as <- allocaArray n $ flip with $ \ptr -> do+    rtmidi_get_compiled_api ptr+    x <- peek ptr+    peekArray n x+  pure (map (toEnum . fromEnum) as) --- |Open a MIDI connection-openPort :: Device+-- | Open a MIDI connection+openPort :: (MonadIO m, IsDevice d)+         => d          -> Int          -- ^ port number          -> String       -- ^ name for the application port that is used-         -> IO ()-openPort d n name = withCString name $ rtmidi_open_port (device d) (toEnum n)+         -> m ()+openPort d n name = liftIO $ do+  let dptr = toDevicePtr d+  withCString name (rtmidi_open_port dptr (toEnum n))+  guardError dptr --- |This function creates a virtual MIDI output port to which other software applications can connect.+-- | This function creates a virtual MIDI output port to which other software applications can connect. -- -- This type of functionality is currently only supported by the Macintosh OS X, Linux ALSA and JACK APIs -- (the function does nothing with the other APIs).-openVirtualPort :: Device -> String -> IO ()-openVirtualPort d name = withCString name $ rtmidi_open_virtual_port (device d)+openVirtualPort :: (MonadIO m, IsDevice d) => d -> String -> m ()+openVirtualPort d name = liftIO $ do+  let dptr = toDevicePtr d+  withCString name (rtmidi_open_virtual_port dptr)+  guardError dptr --- |Close an open MIDI connection (if one exists).-closePort :: Device -> IO ()-closePort d = rtmidi_close_port $ device d+-- | Close an open MIDI connection (if one exists).+closePort :: (MonadIO m, IsDevice d) => d -> m ()+closePort d = liftIO $ do+  let dptr = toDevicePtr d+  rtmidi_close_port dptr+  guardError dptr --- |Return the number of MIDI ports available to the 'Device'.-portCount :: Device -> IO Int-portCount d = fromIntegral <$> (rtmidi_get_port_count $ device d)+-- | Return the number of MIDI ports available to the 'Device'.+portCount :: (MonadIO m, IsDevice d) => d -> m Int+portCount d = liftIO$ do+  let dptr = toDevicePtr d+  x <- rtmidi_get_port_count dptr+  guardError dptr+  pure (fromIntegral x) --- |Return a string identifier for the specified MIDI port number.+-- | Return a string identifier for the specified MIDI port number. ----- An empty string is returned if an invalid port specifier is provided. -portName :: Device -> Int -> IO String-portName d n = peekCString =<< rtmidi_get_port_name (device d) (toEnum n)+-- 'Nothing' is returned if an invalid port specifier is provided.+portName :: (MonadIO m, IsDevice d) => d -> Int -> m (Maybe String)+portName d n = liftIO $ do+  let dptr = toDevicePtr d+  x <- rtmidi_get_port_name dptr (toEnum n)+  guardError dptr+  s <- peekCString x+  case s of+    [] -> pure Nothing+    _ -> pure (Just s) --- |Default constructor for a 'Device' to use for input.-defaultInput :: IO Device-defaultInput = Input <$> rtmidi_in_create_default+-- | Convenience function to list ports.+--+-- Note that the underlying library does not offer an "atomic" interface for this+-- so results may be inconsistent if you connect/disconnect ports during this call.+listPorts :: (MonadIO m, IsDevice d) => d -> m [(Int, String)]+listPorts d = liftIO $ portCount d >>= go [] 0 where+  go acc i c =+    if i >= c+      then pure (reverse acc)+      else do+        mn <- portName d i+        let acc' = maybe acc (\n -> (i, n):acc) mn+        go acc' (succ i) c --- |Create a new 'Device' to use for input.-createInput :: Api        -- ^ API to use+-- | Convenience function to lookup the first port satisfying the predicate.+--+-- You may want to find an exact name:+--+-- > findPort d (== name)+--+-- Or you may want to match part of a name:+--+-- > findPort d (isInfixOf name)+--+-- Note that if you are performing many lookups, it's better to use 'listPorts' and+-- do the lookups yourself (see the caveats there too).+findPort :: (MonadIO m, IsDevice d) => d -> (String -> Bool) -> m (Maybe Int)+findPort d f = liftIO $ portCount d >>= go 0 where+  go i c =+    if i >= c+      then pure Nothing+      else do+        mn <- portName d i+        case mn of+          Just n | f n -> pure (Just i)+          _ -> go (succ i) c++-- | Default constructor for a 'Device' to use for input.+defaultInput :: MonadIO m => m InputDevice+defaultInput = liftIO $ do+  dptr <- rtmidi_in_create_default+  guardError dptr+  pure (InputDevice (Device dptr))++-- | Create a new 'Device' to use for input.+createInput :: MonadIO m+            => Api        -- ^ API to use             -> String     -- ^ client name             -> Int        -- ^ size of the MIDI input queue-            -> IO Device-createInput api clientName queueSizeLimit = Input <$>-   (withCString clientName $ \str -> rtmidi_in_create (toEnum $ fromEnum api) str (toEnum queueSizeLimit))+            -> m InputDevice+createInput api clientName queueSizeLimit = liftIO $ do+  dptr <- withCString clientName (\str -> rtmidi_in_create (toEnum $ fromEnum api) str (toEnum queueSizeLimit))+  guardError dptr+  pure (InputDevice (Device dptr))  foreign import ccall "wrapper"-  wrap :: (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()) -> IO (FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()))+  mkCallbackPointer :: (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()) -> IO (FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ())) -proxy :: (CDouble -> [CUChar] -> Ptr () -> IO ()) -> (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ())-proxy f t d s p = peekArray (fromIntegral s) d >>= \a -> f t a p+adaptCallbackCTypes :: (Double -> [Word8] -> IO ()) -> (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ())+adaptCallbackCTypes f (CDouble t) d s _ = peekArray (fromIntegral s) d >>= \a -> f t (coerce a) --- |Set a callback function to be invoked for incoming MIDI messages.--- +-- | Set a callback function to be invoked for incoming MIDI messages.+-- -- The callback function will be called whenever an incoming MIDI message is received. -- While not absolutely necessary, it is best to set the callback function before opening a MIDI port to avoid leaving -- some messages in the queue.-setCallback :: Device-            -> (CDouble -> [CUChar] -> IO ())  -- ^ Function that takes a timestamp and a MIDI message as arguments-            -> IO ()-setCallback d c = flip (rtmidi_in_set_callback (toInput d)) nullPtr =<< wrap (proxy ((const .) . c))----- |See `setCallback`.------ Additionally a 'Ptr ()' is passed to the callback function whenever it is called.-setCallbackWithUserData :: Device-                        -> (CDouble -> [CUChar] -> Ptr () -> IO ())-                        -> Ptr ()-                        -> IO ()-setCallbackWithUserData d c u = flip (rtmidi_in_set_callback (toInput d)) u =<< (wrap $ proxy c)+setCallback :: MonadUnliftIO m+            => InputDevice+            -> (Double -> [Word8] -> m ())  -- ^ Function that takes a timestamp and a MIDI message as arguments+            -> m ()+setCallback d c = withRunInIO $ \run -> do+  let dptr = toDevicePtr d+  f <- mkCallbackPointer (adaptCallbackCTypes (\ts bytes -> run (c ts bytes)))+  rtmidi_in_set_callback dptr f nullPtr+  guardError dptr --- |Cancel use of the current callback function (if one exists).+-- | Cancel use of the current callback function (if one exists). -- -- Subsequent incoming MIDI messages will be written to the queue and can be retrieved with the `getMessage` function.-cancelCallback :: Device -> IO ()-cancelCallback d = rtmidi_in_cancel_callback (toInput d)+cancelCallback :: MonadIO m => InputDevice -> m ()+cancelCallback d = liftIO $ do+  let dptr = toDevicePtr d+  rtmidi_in_cancel_callback dptr+  guardError dptr --- |Specify whether certain MIDI message types should be queued or ignored during input. +-- | Specify whether certain MIDI message types should be queued or ignored during input. -- -- By default, MIDI timing and active sensing messages are ignored during message input because of their -- relative high data rates. MIDI sysex messages are ignored by default as well. -- Variable values of `true` imply that the respective message type will be ignored.-ignoreTypes :: Device+ignoreTypes :: MonadIO m+            => InputDevice             -> Bool       -- ^ SysEx messages             -> Bool       -- ^ Time messages             -> Bool       -- ^ Sense messages-            -> IO ()-ignoreTypes d sysex time sense = rtmidi_in_ignore_types (toInput d) sysex time sense+            -> m ()+ignoreTypes d x y z = liftIO (rtmidi_in_ignore_types (toDevicePtr d) x y z) --- |Return data bytes for the next available MIDI message in the input queue and the event delta-time in seconds.+-- | Variant of 'getMessage' that allows you to set message buffer size (typically for large sysex messages).+getMessageSized :: MonadIO m => InputDevice -> Int -> m (Double, [Word8])+getMessageSized d n = liftIO $ alloca $ \s -> allocaArray n $ flip with $ \m -> do+  let dptr = toDevicePtr d+  poke s (fromIntegral n)+  CDouble timestamp <- rtmidi_in_get_message dptr m s+  guardError dptr+  size <- peek s+  message <-+    case size of+      0 -> pure []+      _ -> do+        x <- peek m+        y <- peekArray (fromIntegral size) x+        pure (coerce y)+  pure (timestamp, message)++-- | Return data bytes for the next available MIDI message in the input queue and the event delta-time in seconds. -- -- This function returns immediately whether a new message is available or not. -- A valid message is indicated by whether the list contains any elements.-getMessage :: Device -> IO ([CUChar], Double)-getMessage d = alloca $ \m -> alloca $ \s -> do-   timestamp <- rtmidi_in_get_message (toInput d) m s-   size <- peek s-   message <- peekArray (fromIntegral size) =<< peek m-   return (message, toEnum $ fromEnum timestamp)+-- Note that large sysex messages will be silently dropped! Use 'getMessageSized' or use a callback to get these safely.+getMessage :: MonadIO m => InputDevice -> m (Double, [Word8])+getMessage d = liftIO (getMessageSized d defaultMessageSize) --- |Default constructor for a 'Device' to use for output.-defaultOutput :: IO Device-defaultOutput = Output <$> rtmidi_out_create_default+-- | Default constructor for a 'Device' to use for output.+defaultOutput :: MonadIO m => m OutputDevice+defaultOutput = liftIO $ do+  dptr <- rtmidi_out_create_default+  guardError dptr+  pure (OutputDevice (Device dptr)) --- |Create a new 'Device' to use for output.-createOutput :: Api        -- ^ API to use+-- | Create a new 'Device' to use for output.+createOutput :: MonadIO m+             => Api        -- ^ API to use              -> String     -- ^ client name-             -> IO Device-createOutput api clientName = Output <$>-   (withCString clientName $ rtmidi_out_create (toEnum (fromEnum api)))---- |Immediately send a single message out an open MIDI output port. -sendMessage :: Device -> [CUChar] -> IO ()-sendMessage d m = withArrayLen m $-   \n ptr -> rtmidi_out_send_message (toOutput d) ptr (fromIntegral n) >> return ()---- |If a MIDI connection is still open, it will be closed-closeInput (Input x) = rtmidi_in_free x+             -> m OutputDevice+createOutput api clientName = liftIO $ do+  dptr <- withCString clientName (rtmidi_out_create (toEnum (fromEnum api)))+  guardError dptr+  pure (OutputDevice (Device dptr)) --- |Close any open MIDI connections-closeOutput (Output x) = rtmidi_out_free x+-- | Immediately send a single message out an open MIDI output port.+sendMessage :: MonadIO m => OutputDevice -> [Word8] -> m ()+sendMessage d m = liftIO $ withArrayLen m $ \n ptr -> do+  let dptr = toDevicePtr d+  rtmidi_out_send_message dptr (coerce ptr) (fromIntegral n)+  guardError dptr --- |Returns the specifier for the MIDI 'Api' in use-currentApi :: Device -> IO Api-currentApi d = (toEnum . fromEnum) <$>-   case d of-      Input x -> rtmidi_in_get_current_api x-      Output x -> rtmidi_out_get_current_api x+-- | If a MIDI connection is still open, it will be closed+closeDevice :: MonadIO m => IsDevice d => d -> m ()+closeDevice d = liftIO $+  let dptr = toDevicePtr d+  in case getDeviceType d of+    InputDeviceType -> rtmidi_in_free dptr+    OutputDeviceType -> rtmidi_out_free dptr +-- | Returns the specifier for the MIDI 'Api' in use+currentApi :: MonadIO m => IsDevice d => d -> m Api+currentApi d = liftIO $ do+  let dptr = toDevicePtr d+  res <-+    case getDeviceType d of+      InputDeviceType -> rtmidi_in_get_current_api dptr+      OutputDeviceType -> rtmidi_out_get_current_api dptr+  guardError dptr+  pure (toEnum (fromEnum res))
+ Sound/RtMidi/Foreign.hsc view
@@ -0,0 +1,110 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ForeignFunctionInterface #-}++-- | FFI defs for RtMidi+module Sound.RtMidi.Foreign+  ( Wrapper (..)+  , rtmidi_close_port+  , rtmidi_get_compiled_api+  , rtmidi_get_port_count+  , rtmidi_get_port_name+  , rtmidi_in_cancel_callback+  , rtmidi_in_create+  , rtmidi_in_create_default+  , rtmidi_in_free+  , rtmidi_in_get_current_api+  , rtmidi_in_get_message+  , rtmidi_in_ignore_types+  , rtmidi_in_set_callback+  , rtmidi_open_port+  , rtmidi_open_virtual_port+  , rtmidi_out_create+  , rtmidi_out_create_default+  , rtmidi_out_free+  , rtmidi_out_get_current_api+  , rtmidi_out_send_message+  ) where++#include "rtmidi_c.h"++import Foreign (FunPtr, Ptr, Storable (..))+import Foreign.C (CDouble (..), CInt (..), CString, CSize, CUChar)++data Wrapper = Wrapper+  { ptr :: !(Ptr ())+  , dat :: !(Ptr ())+  , ok  :: !Bool+  , msg :: !CString+  } deriving (Eq, Show)++instance Storable Wrapper where+  sizeOf _ = #{size struct RtMidiWrapper}+  alignment _ = #{alignment struct RtMidiWrapper}+  poke ptr (Wrapper a b c d) = do+    #{poke struct RtMidiWrapper, ptr} ptr a+    #{poke struct RtMidiWrapper, data} ptr b+    #{poke struct RtMidiWrapper, ok} ptr c+    #{poke struct RtMidiWrapper, msg} ptr d+  peek ptr = do+    a <- #{peek struct RtMidiWrapper, ptr} ptr+    b <- #{peek struct RtMidiWrapper, data} ptr+    c <- #{peek struct RtMidiWrapper, ok} ptr+    d <- #{peek struct RtMidiWrapper, msg} ptr+    pure (Wrapper a b c d)++foreign import ccall "rtmidi_c.h rtmidi_close_port"+  rtmidi_close_port :: Ptr Wrapper -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_get_compiled_api"+  rtmidi_get_compiled_api :: Ptr (Ptr CInt) -> IO CInt++foreign import ccall "rtmidi_c.h rtmidi_get_port_count"+  rtmidi_get_port_count :: Ptr Wrapper -> IO CInt++foreign import ccall "rtmidi_c.h rtmidi_get_port_name"+  rtmidi_get_port_name :: Ptr Wrapper -> CInt -> IO CString++foreign import ccall "rtmidi_c.h rtmidi_in_cancel_callback"+  rtmidi_in_cancel_callback :: Ptr Wrapper -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_in_create"+  rtmidi_in_create :: CInt -> CString -> CInt -> IO (Ptr Wrapper)++foreign import ccall "rtmidi_c.h rtmidi_in_create_default"+  rtmidi_in_create_default :: IO (Ptr Wrapper)++foreign import ccall "rtmidi_c.h rtmidi_in_free"+  rtmidi_in_free :: Ptr Wrapper -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_in_get_current_api"+  rtmidi_in_get_current_api :: Ptr Wrapper -> IO CInt++foreign import ccall "rtmidi_c.h rtmidi_in_get_message"+  rtmidi_in_get_message :: Ptr Wrapper -> Ptr (Ptr CUChar) -> Ptr CSize -> IO CDouble++foreign import ccall "rtmidi_c.h rtmidi_in_ignore_types"+  rtmidi_in_ignore_types :: Ptr Wrapper -> Bool -> Bool -> Bool -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_in_set_callback"+  rtmidi_in_set_callback :: Ptr Wrapper -> FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()) -> Ptr () -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_open_port"+  rtmidi_open_port :: Ptr Wrapper -> CInt -> CString -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_open_virtual_port"+  rtmidi_open_virtual_port :: Ptr Wrapper -> CString -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_out_create"+  rtmidi_out_create :: CInt -> CString -> IO (Ptr Wrapper)++foreign import ccall "rtmidi_c.h rtmidi_out_create_default"+  rtmidi_out_create_default :: IO (Ptr Wrapper)++foreign import ccall "rtmidi_c.h rtmidi_out_free"+  rtmidi_out_free :: Ptr Wrapper -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_out_get_current_api"+  rtmidi_out_get_current_api :: Ptr Wrapper -> IO CInt++foreign import ccall "rtmidi_c.h rtmidi_out_send_message"+  rtmidi_out_send_message :: Ptr Wrapper -> Ptr CUChar -> CInt -> IO CInt
examples/callback.hs view
@@ -1,13 +1,16 @@-import Sound.RtMidi-import Numeric-import Foreign.C+import Data.Word (Word8)+import Sound.RtMidi (closeDevice, closePort, defaultInput, openPort, setCallback)+import Numeric (showHex) -callback :: CDouble -> [CUChar] -> IO ()+callback :: Double -> [Word8] -> IO () callback delta msg = putStrLn $ (foldr (showHex . fromEnum) "" msg) ++ " - " ++ (show delta) +main :: IO () main = do   i <- defaultInput   openPort i 0 "RtMidi"   setCallback i callback   _ <- getLine+  closePort i+  closeDevice i   return ()
examples/playback.hs view
@@ -1,6 +1,7 @@-import Sound.RtMidi-import Control.Concurrent+import Sound.RtMidi (closeDevice, closePort, defaultOutput, openPort, sendMessage, portCount, portName)+import Control.Concurrent (threadDelay) +main :: IO () main = do   device <- defaultOutput   numPorts <- portCount device@@ -17,4 +18,4 @@   mapM_ (\x -> sendMessage device [0x90, x, 0x7f] >> threadDelay 120000) $ take 240 song   putStrLn "exiting..."   closePort device-  closeOutput device+  closeDevice device
+ examples/poll.hs view
@@ -0,0 +1,21 @@+import Sound.RtMidi (InputDevice, closeDevice, closePort, defaultInput, getMessage, openPort)+import Control.Concurrent (forkIO, killThread)+import Control.Monad (forever)++mainLoop :: InputDevice -> IO ()+mainLoop d = do+  m <- getMessage d+  if length (snd m) > 0 then+    putStrLn $ show m+  else return ()++main :: IO ()+main = do+  i <- defaultInput+  openPort i 0 "RtMidi"+  id <- forkIO $ forever $ mainLoop i+  _ <- getLine+  killThread id+  closePort i+  closeDevice i+  return ()
examples/query.hs view
@@ -1,14 +1,12 @@-import Sound.RtMidi-import Control.Concurrent+import Sound.RtMidi (closeDevice, compiledApis, createOutput, currentApi, defaultOutput, listPorts) +main :: IO () main = do-  device <- createOutput AlsaApi "test"+  device <- defaultOutput   api <- currentApi device   builtin <- compiledApis-  numPorts <- portCount device-  portNames <- mapM (portName device) [0..numPorts-1]+  portPairs <- listPorts device   putStrLn $ "RtMidi output using " ++ (show api)   putStrLn $ "built-in: " ++ (show builtin)-  putStrLn $ "available Ports: " ++ (show numPorts)-  putStrLn (show portNames)-  closeOutput device+  putStrLn $ "available ports: " ++ (show portPairs)+  closeDevice device
rtmidi/RtMidi.cpp view
@@ -5,2838 +5,3440 @@     This class implements some common functionality for the realtime     MIDI input/output subclasses RtMidiIn and RtMidiOut. -    RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/--    RtMidi: realtime MIDI i/o C++ classes-    Copyright (c) 2003-2016 Gary P. Scavone--    Permission is hereby granted, free of charge, to any person-    obtaining a copy of this software and associated documentation files-    (the "Software"), to deal in the Software without restriction,-    including without limitation the rights to use, copy, modify, merge,-    publish, distribute, sublicense, and/or sell copies of the Software,-    and to permit persons to whom the Software is furnished to do so,-    subject to the following conditions:--    The above copyright notice and this permission notice shall be-    included in all copies or substantial portions of the Software.--    Any person wishing to distribute modifications to the Software is-    asked to send the modifications to the original developer so that-    they can be incorporated into the canonical version.  This is,-    however, not a binding provision of this license.--    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.-    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR-    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF-    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION-    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.-*/-/**********************************************************************/--#include "RtMidi.h"-#include <sstream>--#if defined(__MACOSX_CORE__)-  #if TARGET_OS_IPHONE-    #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime-    #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos-  #endif-#endif--//*********************************************************************//-//  RtMidi Definitions-//*********************************************************************//--RtMidi :: RtMidi()-  : rtapi_(0)-{-}--RtMidi :: ~RtMidi()-{-  if ( rtapi_ )-    delete rtapi_;-  rtapi_ = 0;-}--std::string RtMidi :: getVersion( void ) throw()-{-  return std::string( RTMIDI_VERSION );-}--void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()-{-  apis.clear();--  // The order here will control the order of RtMidi's API search in-  // the constructor.-#if defined(__MACOSX_CORE__)-  apis.push_back( MACOSX_CORE );-#endif-#if defined(__LINUX_ALSA__)-  apis.push_back( LINUX_ALSA );-#endif-#if defined(__UNIX_JACK__)-  apis.push_back( UNIX_JACK );-#endif-#if defined(__WINDOWS_MM__)-  apis.push_back( WINDOWS_MM );-#endif-#if defined(__RTMIDI_DUMMY__)-  apis.push_back( RTMIDI_DUMMY );-#endif-}--//*********************************************************************//-//  RtMidiIn Definitions-//*********************************************************************//--void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit )-{-  if ( rtapi_ )-    delete rtapi_;-  rtapi_ = 0;--#if defined(__UNIX_JACK__)-  if ( api == UNIX_JACK )-    rtapi_ = new MidiInJack( clientName, queueSizeLimit );-#endif-#if defined(__LINUX_ALSA__)-  if ( api == LINUX_ALSA )-    rtapi_ = new MidiInAlsa( clientName, queueSizeLimit );-#endif-#if defined(__WINDOWS_MM__)-  if ( api == WINDOWS_MM )-    rtapi_ = new MidiInWinMM( clientName, queueSizeLimit );-#endif-#if defined(__MACOSX_CORE__)-  if ( api == MACOSX_CORE )-    rtapi_ = new MidiInCore( clientName, queueSizeLimit );-#endif-#if defined(__RTMIDI_DUMMY__)-  if ( api == RTMIDI_DUMMY )-    rtapi_ = new MidiInDummy( clientName, queueSizeLimit );-#endif-}--RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit )-  : RtMidi()-{-  if ( api != UNSPECIFIED ) {-    // Attempt to open the specified API.-    openMidiApi( api, clientName, queueSizeLimit );-    if ( rtapi_ ) return;--    // No compiled support for specified API value.  Issue a warning-    // and continue as if no API was specified.-    std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl;-  }--  // Iterate through the compiled APIs and return as soon as we find-  // one with at least one port or we reach the end of the list.-  std::vector< RtMidi::Api > apis;-  getCompiledApi( apis );-  for ( unsigned int i=0; i<apis.size(); i++ ) {-    openMidiApi( apis[i], clientName, queueSizeLimit );-    if ( rtapi_->getPortCount() ) break;-  }--  if ( rtapi_ ) return;--  // It should not be possible to get here because the preprocessor-  // definition __RTMIDI_DUMMY__ is automatically defined if no-  // API-specific definitions are passed to the compiler. But just in-  // case something weird happens, we'll throw an error.-  std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!";-  throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );-}--RtMidiIn :: ~RtMidiIn() throw()-{-}---//*********************************************************************//-//  RtMidiOut Definitions-//*********************************************************************//--void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName )-{-  if ( rtapi_ )-    delete rtapi_;-  rtapi_ = 0;--#if defined(__UNIX_JACK__)-  if ( api == UNIX_JACK )-    rtapi_ = new MidiOutJack( clientName );-#endif-#if defined(__LINUX_ALSA__)-  if ( api == LINUX_ALSA )-    rtapi_ = new MidiOutAlsa( clientName );-#endif-#if defined(__WINDOWS_MM__)-  if ( api == WINDOWS_MM )-    rtapi_ = new MidiOutWinMM( clientName );-#endif-#if defined(__MACOSX_CORE__)-  if ( api == MACOSX_CORE )-    rtapi_ = new MidiOutCore( clientName );-#endif-#if defined(__RTMIDI_DUMMY__)-  if ( api == RTMIDI_DUMMY )-    rtapi_ = new MidiOutDummy( clientName );-#endif-}--RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName )-{-  if ( api != UNSPECIFIED ) {-    // Attempt to open the specified API.-    openMidiApi( api, clientName );-    if ( rtapi_ ) return;--    // No compiled support for specified API value.  Issue a warning-    // and continue as if no API was specified.-    std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl;-  }--  // Iterate through the compiled APIs and return as soon as we find-  // one with at least one port or we reach the end of the list.-  std::vector< RtMidi::Api > apis;-  getCompiledApi( apis );-  for ( unsigned int i=0; i<apis.size(); i++ ) {-    openMidiApi( apis[i], clientName );-    if ( rtapi_->getPortCount() ) break;-  }--  if ( rtapi_ ) return;--  // It should not be possible to get here because the preprocessor-  // definition __RTMIDI_DUMMY__ is automatically defined if no-  // API-specific definitions are passed to the compiler. But just in-  // case something weird happens, we'll thrown an error.-  std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!";-  throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );-}--RtMidiOut :: ~RtMidiOut() throw()-{-}--//*********************************************************************//-//  Common MidiApi Definitions-//*********************************************************************//--MidiApi :: MidiApi( void )-  : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0)-{-}--MidiApi :: ~MidiApi( void )-{-}--void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 )-{-    errorCallback_ = errorCallback;-    errorCallbackUserData_ = userData;-}--void MidiApi :: error( RtMidiError::Type type, std::string errorString )-{-  if ( errorCallback_ ) {--    if ( firstErrorOccurred_ )-      return;--    firstErrorOccurred_ = true;-    const std::string errorMessage = errorString;--    errorCallback_( type, errorMessage, errorCallbackUserData_);-    firstErrorOccurred_ = false;-    return;-  }--  if ( type == RtMidiError::WARNING ) {-    std::cerr << '\n' << errorString << "\n\n";-  }-  else if ( type == RtMidiError::DEBUG_WARNING ) {-#if defined(__RTMIDI_DEBUG__)-    std::cerr << '\n' << errorString << "\n\n";-#endif-  }-  else {-    std::cerr << '\n' << errorString << "\n\n";-    throw RtMidiError( errorString, type );-  }-}--//*********************************************************************//-//  Common MidiInApi Definitions-//*********************************************************************//--MidiInApi :: MidiInApi( unsigned int queueSizeLimit )-  : MidiApi()-{-  // Allocate the MIDI queue.-  inputData_.queue.ringSize = queueSizeLimit;-  if ( inputData_.queue.ringSize > 0 )-    inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ];-}--MidiInApi :: ~MidiInApi( void )-{-  // Delete the MIDI queue.-  if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring;-}--void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData )-{-  if ( inputData_.usingCallback ) {-    errorString_ = "MidiInApi::setCallback: a callback function is already set!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  if ( !callback ) {-    errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  inputData_.userCallback = callback;-  inputData_.userData = userData;-  inputData_.usingCallback = true;-}--void MidiInApi :: cancelCallback()-{-  if ( !inputData_.usingCallback ) {-    errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  inputData_.userCallback = 0;-  inputData_.userData = 0;-  inputData_.usingCallback = false;-}--void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )-{-  inputData_.ignoreFlags = 0;-  if ( midiSysex ) inputData_.ignoreFlags = 0x01;-  if ( midiTime ) inputData_.ignoreFlags |= 0x02;-  if ( midiSense ) inputData_.ignoreFlags |= 0x04;-}--double MidiInApi :: getMessage( std::vector<unsigned char> *message )-{-  message->clear();--  if ( inputData_.usingCallback ) {-    errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";-    error( RtMidiError::WARNING, errorString_ );-    return 0.0;-  }--  if ( inputData_.queue.size == 0 ) return 0.0;--  // Copy queued message to the vector pointer argument and then "pop" it.-  std::vector<unsigned char> *bytes = &(inputData_.queue.ring[inputData_.queue.front].bytes);-  message->assign( bytes->begin(), bytes->end() );-  double deltaTime = inputData_.queue.ring[inputData_.queue.front].timeStamp;-  inputData_.queue.size--;-  inputData_.queue.front++;-  if ( inputData_.queue.front == inputData_.queue.ringSize )-    inputData_.queue.front = 0;--  return deltaTime;-}--//*********************************************************************//-//  Common MidiOutApi Definitions-//*********************************************************************//--MidiOutApi :: MidiOutApi( void )-  : MidiApi()-{-}--MidiOutApi :: ~MidiOutApi( void )-{-}--// *************************************************** //-//-// OS/API-specific methods.-//-// *************************************************** //--#if defined(__MACOSX_CORE__)--// The CoreMIDI API is based on the use of a callback function for-// 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>--// A structure to hold variables related to the CoreMIDI API-// implementation.-struct CoreMidiData {-  MIDIClientRef client;-  MIDIPortRef port;-  MIDIEndpointRef endpoint;-  MIDIEndpointRef destinationId;-  unsigned long long lastTime;-  MIDISysexSendRequest sysexreq;-};--//*********************************************************************//-//  API: OS-X-//  Class Definitions: MidiInCore-//*********************************************************************//--static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ )-{-  MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef);-  CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);--  unsigned char status;-  unsigned short nBytes, iByte, size;-  unsigned long long time;--  bool& continueSysex = data->continueSysex;-  MidiInApi::MidiMessage& message = data->message;--  const MIDIPacket *packet = &list->packet[0];-  for ( unsigned int i=0; i<list->numPackets; ++i ) {--    // My interpretation of the CoreMIDI documentation: all message-    // types, except sysex, are complete within a packet and there may-    // be several of them in a single packet.  Sysex messages can be-    // broken across multiple packets and PacketLists but are bundled-    // alone within each packet (these packets do not contain other-    // message types).  If sysex messages are split across multiple-    // MIDIPacketLists, they must be handled by multiple calls to this-    // function.--    nBytes = packet->length;-    if ( nBytes == 0 ) continue;--    // Calculate time stamp.--    if ( data->firstMessage ) {-      message.timeStamp = 0.0;-      data->firstMessage = false;-    }-    else {-      time = packet->timeStamp;-      if ( time == 0 ) { // this happens when receiving asynchronous sysex messages-        time = AudioGetCurrentHostTime();-      }-      time -= apiData->lastTime;-      time = AudioConvertHostTimeToNanos( time );-      if ( !continueSysex )-        message.timeStamp = time * 0.000000001;-    }-    apiData->lastTime = packet->timeStamp;-    if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages-      apiData->lastTime = AudioGetCurrentHostTime();-    }-    //std::cout << "TimeStamp = " << packet->timeStamp << std::endl;--    iByte = 0;-    if ( continueSysex ) {-      // We have a continuing, segmented sysex message.-      if ( !( data->ignoreFlags & 0x01 ) ) {-        // If we're not ignoring sysex messages, copy the entire packet.-        for ( unsigned int j=0; j<nBytes; ++j )-          message.bytes.push_back( packet->data[j] );-      }-      continueSysex = packet->data[nBytes-1] != 0xF7;--      if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) {-        // If not a continuing sysex message, invoke the user callback function or queue the message.-        if ( data->usingCallback ) {-          RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;-          callback( message.timeStamp, &message.bytes, data->userData );-        }-        else {-          // As long as we haven't reached our queue size limit, push the message.-          if ( data->queue.size < data->queue.ringSize ) {-            data->queue.ring[data->queue.back++] = message;-            if ( data->queue.back == data->queue.ringSize )-              data->queue.back = 0;-            data->queue.size++;-          }-          else-            std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";-        }-        message.bytes.clear();-      }-    }-    else {-      while ( iByte < nBytes ) {-        size = 0;-        // We are expecting that the next byte in the packet is a status byte.-        status = packet->data[iByte];-        if ( !(status & 0x80) ) break;-        // Determine the number of bytes in the MIDI message.-        if ( status < 0xC0 ) size = 3;-        else if ( status < 0xE0 ) size = 2;-        else if ( status < 0xF0 ) size = 3;-        else if ( status == 0xF0 ) {-          // A MIDI sysex-          if ( data->ignoreFlags & 0x01 ) {-            size = 0;-            iByte = nBytes;-          }-          else size = nBytes - iByte;-          continueSysex = packet->data[nBytes-1] != 0xF7;-        }-        else if ( status == 0xF1 ) {-            // A MIDI time code message-           if ( data->ignoreFlags & 0x02 ) {-            size = 0;-            iByte += 2;-           }-           else size = 2;-        }-        else if ( status == 0xF2 ) size = 3;-        else if ( status == 0xF3 ) size = 2;-        else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {-          // A MIDI timing tick message and we're ignoring it.-          size = 0;-          iByte += 1;-        }-        else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {-          // A MIDI active sensing message and we're ignoring it.-          size = 0;-          iByte += 1;-        }-        else size = 1;--        // Copy the MIDI data to our vector.-        if ( size ) {-          message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );-          if ( !continueSysex ) {-            // If not a continuing sysex message, invoke the user callback function or queue the message.-            if ( data->usingCallback ) {-              RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;-              callback( message.timeStamp, &message.bytes, data->userData );-            }-            else {-              // As long as we haven't reached our queue size limit, push the message.-              if ( data->queue.size < data->queue.ringSize ) {-                data->queue.ring[data->queue.back++] = message;-                if ( data->queue.back == data->queue.ringSize )-                  data->queue.back = 0;-                data->queue.size++;-              }-              else-                std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";-            }-            message.bytes.clear();-          }-          iByte += size;-        }-      }-    }-    packet = MIDIPacketNext(packet);-  }-}--MidiInCore :: MidiInCore( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )-{-  initialize( clientName );-}--MidiInCore :: ~MidiInCore( void )-{-  // Close a connection if it exists.-  closePort();--  // Cleanup.-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);-  MIDIClientDispose( data->client );-  if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );-  delete data;-}--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;-  }--  // Save our api-specific connection information.-  CoreMidiData *data = (CoreMidiData *) new CoreMidiData;-  data->client = client;-  data->endpoint = 0;-  apiData_ = (void *) data;-  inputData_.apiData = (void *) data;-  CFRelease(name);-}--void MidiInCore :: openPort( unsigned int portNumber, const std::string portName )-{-  if ( connected_ ) {-    errorString_ = "MidiInCore::openPort: a valid connection already exists!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );-  unsigned int nSrc = MIDIGetNumberOfSources();-  if (nSrc < 1) {-    errorString_ = "MidiInCore::openPort: no MIDI input sources found!";-    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );-    return;-  }--  if ( portNumber >= nSrc ) {-    std::ostringstream ost;-    ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::INVALID_PARAMETER, errorString_ );-    return;-  }--  MIDIPortRef port;-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);-  OSStatus result = MIDIInputPortCreate( data->client, -                                         CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),-                                         midiInputCallback, (void *)&inputData_, &port );-  if ( result != noErr ) {-    MIDIClientDispose( data->client );-    errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Get the desired input source identifier.-  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;-  }--  // Make the connection.-  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;-  }--  // Save our api-specific port information.-  data->port = port;--  connected_ = true;-}--void MidiInCore :: openVirtualPort( const std::string portName )-{-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);--  // Create a virtual MIDI input destination.-  MIDIEndpointRef endpoint;-  OSStatus result = MIDIDestinationCreate( data->client,-                                           CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),-                                           midiInputCallback, (void *)&inputData_, &endpoint );-  if ( result != noErr ) {-    errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Save our api-specific connection information.-  data->endpoint = endpoint;-}--void MidiInCore :: closePort( void )-{-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);--  if ( data->endpoint ) {-    MIDIEndpointDispose( data->endpoint );-  }--  if ( data->port ) {-    MIDIPortDispose( data->port );-  }--  connected_ = false;-}--unsigned int MidiInCore :: getPortCount()-{-  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );-  return MIDIGetNumberOfSources();-}--// This function was submitted by Douglas Casey Tucker and apparently-// derived largely from PortMidi.-CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )-{-  CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );-  CFStringRef str;--  // Begin with the endpoint's name.-  str = NULL;-  MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );-  if ( str != NULL ) {-    CFStringAppend( result, str );-    CFRelease( str );-  }--  MIDIEntityRef entity = 0;-  MIDIEndpointGetEntity( endpoint, &entity );-  if ( entity == 0 )-    // probably virtual-    return result;--  if ( CFStringGetLength( result ) == 0 ) {-    // endpoint name has zero length -- try the entity-    str = NULL;-    MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );-    if ( str != NULL ) {-      CFStringAppend( result, str );-      CFRelease( str );-    }-  }-  // now consider the device's name-  MIDIDeviceRef device = 0;-  MIDIEntityGetDevice( entity, &device );-  if ( device == 0 )-    return result;--  str = NULL;-  MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );-  if ( CFStringGetLength( result ) == 0 ) {-      CFRelease( result );-      return str;-  }-  if ( str != NULL ) {-    // if an external device has only one entity, throw away-    // the endpoint name and just use the device name-    if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {-      CFRelease( result );-      return str;-    } else {-      if ( CFStringGetLength( str ) == 0 ) {-        CFRelease( str );-        return result;-      }-      // does the entity name already start with the device name?-      // (some drivers do this though they shouldn't)-      // if so, do not prepend-        if ( CFStringCompareWithOptions( result, /* endpoint name */-             str /* device name */,-             CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {-        // prepend the device name to the entity name-        if ( CFStringGetLength( result ) > 0 )-          CFStringInsert( result, 0, CFSTR(" ") );-        CFStringInsert( result, 0, str );-      }-      CFRelease( str );-    }-  }-  return result;-}--// This function was submitted by Douglas Casey Tucker and apparently-// derived largely from PortMidi.-static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )-{-  CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );-  CFStringRef str;-  OSStatus err;-  int i;--  // Does the endpoint have connections?-  CFDataRef connections = NULL;-  int nConnected = 0;-  bool anyStrings = false;-  err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );-  if ( connections != NULL ) {-    // It has connections, follow them-    // Concatenate the names of all connected devices-    nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);-    if ( nConnected ) {-      const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));-      for ( i=0; i<nConnected; ++i, ++pid ) {-        MIDIUniqueID id = EndianS32_BtoN( *pid );-        MIDIObjectRef connObject;-        MIDIObjectType connObjectType;-        err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );-        if ( err == noErr ) {-          if ( connObjectType == kMIDIObjectType_ExternalSource  ||-              connObjectType == kMIDIObjectType_ExternalDestination ) {-            // Connected to an external device's endpoint (10.3 and later).-            str = EndpointName( (MIDIEndpointRef)(connObject), true );-          } else {-            // Connected to an external device (10.2) (or something else, catch--            str = NULL;-            MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );-          }-          if ( str != NULL ) {-            if ( anyStrings )-              CFStringAppend( result, CFSTR(", ") );-            else anyStrings = true;-            CFStringAppend( result, str );-            CFRelease( str );-          }-        }-      }-    }-    CFRelease( connections );-  }-  if ( anyStrings )-    return result;--  CFRelease( result );--  // Here, either the endpoint had no connections, or we failed to obtain names -  return EndpointName( endpoint, false );-}--std::string MidiInCore :: getPortName( unsigned int portNumber )-{-  CFStringRef nameRef;-  MIDIEndpointRef portRef;-  char name[128];--  std::string stringName;-  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );-  if ( portNumber >= MIDIGetNumberOfSources() ) {-    std::ostringstream ost;-    ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::WARNING, errorString_ );-    return stringName;-  }--  portRef = MIDIGetSource( portNumber );-  nameRef = ConnectedEndpointName(portRef);-  CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding());-  CFRelease( nameRef );--  return stringName = name;-}--//*********************************************************************//-//  API: OS-X-//  Class Definitions: MidiOutCore-//*********************************************************************//--MidiOutCore :: MidiOutCore( const std::string clientName ) : MidiOutApi()-{-  initialize( clientName );-}--MidiOutCore :: ~MidiOutCore( void )-{-  // Close a connection if it exists.-  closePort();--  // Cleanup.-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);-  MIDIClientDispose( data->client );-  if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );-  delete data;-}--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;-  }--  // 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()-{-  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );-  return MIDIGetNumberOfDestinations();-}--std::string MidiOutCore :: getPortName( unsigned int portNumber )-{-  CFStringRef nameRef;-  MIDIEndpointRef portRef;-  char name[128];--  std::string stringName;-  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );-  if ( portNumber >= MIDIGetNumberOfDestinations() ) {-    std::ostringstream ost;-    ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::WARNING, errorString_ );-    return stringName;-  }--  portRef = MIDIGetDestination( portNumber );-  nameRef = ConnectedEndpointName(portRef);-  CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding());-  CFRelease( nameRef );-  -  return stringName = name;-}--void MidiOutCore :: openPort( unsigned int portNumber, const std::string portName )-{-  if ( connected_ ) {-    errorString_ = "MidiOutCore::openPort: a valid connection already exists!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );-  unsigned int nDest = MIDIGetNumberOfDestinations();-  if (nDest < 1) {-    errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!";-    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );-    return;-  }--  if ( portNumber >= nDest ) {-    std::ostringstream ost;-    ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::INVALID_PARAMETER, errorString_ );-    return;-  }--  MIDIPortRef port;-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);-  OSStatus result = MIDIOutputPortCreate( data->client, -                                          CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),-                                          &port );-  if ( result != noErr ) {-    MIDIClientDispose( data->client );-    errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Get the desired output port identifier.-  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;-  }--  // Save our api-specific connection information.-  data->port = port;-  data->destinationId = destination;-  connected_ = true;-}--void MidiOutCore :: closePort( void )-{-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);--  if ( data->endpoint ) {-    MIDIEndpointDispose( data->endpoint );-  }--  if ( data->port ) {-    MIDIPortDispose( data->port );-  }--  connected_ = false;-}--void MidiOutCore :: openVirtualPort( std::string portName )-{-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);--  if ( data->endpoint ) {-    errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  // Create a virtual MIDI output source.-  MIDIEndpointRef endpoint;-  OSStatus result = MIDISourceCreate( data->client,-                                      CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),-                                      &endpoint );-  if ( result != noErr ) {-    errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Save our api-specific connection information.-  data->endpoint = endpoint;-}--void MidiOutCore :: sendMessage( std::vector<unsigned char> *message )-{-  // We use the MIDISendSysex() function to asynchronously send sysex-  // messages.  Otherwise, we use a single CoreMidi MIDIPacket.-  unsigned int nBytes = message->size();-  if ( nBytes == 0 ) {-    errorString_ = "MidiOutCore::sendMessage: no data in message argument!";      -    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  MIDITimeStamp timeStamp = AudioGetCurrentHostTime();-  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);-  OSStatus result;--  if ( message->at(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))];-  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-    const Byte* dataStartPtr = (const Byte *) &message->at( 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;-  }--  // 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_ );-    }-  }-}--#endif  // __MACOSX_CORE__---//*********************************************************************//-//  API: LINUX ALSA SEQUENCER-//*********************************************************************//--// API information found at:-//   - http://www.alsa-project.org/documentation.php#Library--#if defined(__LINUX_ALSA__)--// The ALSA Sequencer API is based on the use of a callback function for-// MIDI input.-//-// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer-// time stamps and other assorted fixes!!!--// If you don't need timestamping for incoming MIDI events, define the-// preprocessor definition AVOID_TIMESTAMPING to save resources-// associated with the ALSA sequencer queues.--#include <pthread.h>-#include <sys/time.h>--// ALSA header file.-#include <alsa/asoundlib.h>--// A structure to hold variables related to the ALSA API-// implementation.-struct AlsaMidiData {-  snd_seq_t *seq;-  unsigned int portNum;-  int vport;-  snd_seq_port_subscribe_t *subscription;-  snd_midi_event_t *coder;-  unsigned int bufferSize;-  unsigned char *buffer;-  pthread_t thread;-  pthread_t dummy_thread_id;-  unsigned long long lastTime;-  int queue_id; // an input queue is needed to get timestamped events-  int trigger_fds[2];-};--#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))--//*********************************************************************//-//  API: LINUX ALSA-//  Class Definitions: MidiInAlsa-//*********************************************************************//--static void *alsaMidiHandler( void *ptr )-{-  MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr);-  AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);--  long nBytes;-  unsigned long long time, lastTime;-  bool continueSysex = false;-  bool doDecode = false;-  MidiInApi::MidiMessage message;-  int poll_fd_count;-  struct pollfd *poll_fds;--  snd_seq_event_t *ev;-  int result;-  apiData->bufferSize = 32;-  result = snd_midi_event_new( 0, &apiData->coder );-  if ( result < 0 ) {-    data->doInput = false;-    std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n";-    return 0;-  }-  unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );-  if ( buffer == NULL ) {-    data->doInput = false;-    snd_midi_event_free( apiData->coder );-    apiData->coder = 0;-    std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n";-    return 0;-  }-  snd_midi_event_init( apiData->coder );-  snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages--  poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1;-  poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd ));-  snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN );-  poll_fds[0].fd = apiData->trigger_fds[0];-  poll_fds[0].events = POLLIN;--  while ( data->doInput ) {--    if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {-      // No data pending-      if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) {-        if ( poll_fds[0].revents & POLLIN ) {-          bool dummy;-          int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) );-          (void) res;-        }-      }-      continue;-    }--    // If here, there should be data.-    result = snd_seq_event_input( apiData->seq, &ev );-    if ( result == -ENOSPC ) {-      std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n";-      continue;-    }-    else if ( result <= 0 ) {-      std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n";-      perror("System reports");-      continue;-    }--    // This is a bit weird, but we now have to decode an ALSA MIDI-    // event (back) into MIDI bytes.  We'll ignore non-MIDI types.-    if ( !continueSysex ) message.bytes.clear();--    doDecode = false;-    switch ( ev->type ) {--    case SND_SEQ_EVENT_PORT_SUBSCRIBED:-#if defined(__RTMIDI_DEBUG__)-      std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n";-#endif-      break;--    case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:-#if defined(__RTMIDI_DEBUG__)-      std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n";-      std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"-                << (int) ev->data.connect.sender.port-                << ", dest = " << (int) ev->data.connect.dest.client << ":"-                << (int) ev->data.connect.dest.port-                << std::endl;-#endif-      break;--    case SND_SEQ_EVENT_QFRAME: // MIDI time code-      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;-      break;--    case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick-      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;-      break;--    case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick-      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;-      break;--    case SND_SEQ_EVENT_SENSING: // Active sensing-      if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;-      break;--		case SND_SEQ_EVENT_SYSEX:-      if ( (data->ignoreFlags & 0x01) ) break;-      if ( ev->data.ext.len > apiData->bufferSize ) {-        apiData->bufferSize = ev->data.ext.len;-        free( buffer );-        buffer = (unsigned char *) malloc( apiData->bufferSize );-        if ( buffer == NULL ) {-          data->doInput = false;-          std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n";-          break;-        }-      }--    default:-      doDecode = true;-    }--    if ( doDecode ) {--      nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );-      if ( nBytes > 0 ) {-        // The ALSA sequencer has a maximum buffer size for MIDI sysex-        // events of 256 bytes.  If a device sends sysex messages larger-        // than this, they are segmented into 256 byte chunks.  So,-        // we'll watch for this and concatenate sysex chunks into a-        // single sysex message if necessary.-        if ( !continueSysex )-          message.bytes.assign( buffer, &buffer[nBytes] );-        else-          message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );--        continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );-        if ( !continueSysex ) {--          // Calculate the time stamp:-          message.timeStamp = 0.0;--          // Method 1: Use the system time.-          //(void)gettimeofday(&tv, (struct timezone *)NULL);-          //time = (tv.tv_sec * 1000000) + tv.tv_usec;--          // Method 2: Use the ALSA sequencer event time data.-          // (thanks to Pedro Lopez-Cabanillas!).-          time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );-          lastTime = time;-          time -= apiData->lastTime;-          apiData->lastTime = lastTime;-          if ( data->firstMessage == true )-            data->firstMessage = false;-          else-            message.timeStamp = time * 0.000001;-        }-        else {-#if defined(__RTMIDI_DEBUG__)-          std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";-#endif-        }-      }-    }--    snd_seq_free_event( ev );-    if ( message.bytes.size() == 0 || continueSysex ) continue;--    if ( data->usingCallback ) {-      RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;-      callback( message.timeStamp, &message.bytes, data->userData );-    }-    else {-      // As long as we haven't reached our queue size limit, push the message.-      if ( data->queue.size < data->queue.ringSize ) {-        data->queue.ring[data->queue.back++] = message;-        if ( data->queue.back == data->queue.ringSize )-          data->queue.back = 0;-        data->queue.size++;-      }-      else-        std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n";-    }-  }--  if ( buffer ) free( buffer );-  snd_midi_event_free( apiData->coder );-  apiData->coder = 0;-  apiData->thread = apiData->dummy_thread_id;-  return 0;-}--MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )-{-  initialize( clientName );-}--MidiInAlsa :: ~MidiInAlsa()-{-  // Close a connection if it exists.-  closePort();--  // Shutdown the input thread.-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( inputData_.doInput ) {-    inputData_.doInput = false;-    int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );-    (void) res;-    if ( !pthread_equal(data->thread, data->dummy_thread_id) )-      pthread_join( data->thread, NULL );-  }--  // Cleanup.-  close ( data->trigger_fds[0] );-  close ( data->trigger_fds[1] );-  if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );-#ifndef AVOID_TIMESTAMPING-  snd_seq_free_queue( data->seq, data->queue_id );-#endif-  snd_seq_close( data->seq );-  delete data;-}--void MidiInAlsa :: initialize( const std::string& clientName )-{-  // Set up the ALSA sequencer client.-  snd_seq_t *seq;-  int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);-  if ( result < 0 ) {-    errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Set client name.-  snd_seq_set_client_name( seq, clientName.c_str() );--  // Save our api-specific connection information.-  AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;-  data->seq = seq;-  data->portNum = -1;-  data->vport = -1;-  data->subscription = 0;-  data->dummy_thread_id = pthread_self();-  data->thread = data->dummy_thread_id;-  data->trigger_fds[0] = -1;-  data->trigger_fds[1] = -1;-  apiData_ = (void *) data;-  inputData_.apiData = (void *) data;--   if ( pipe(data->trigger_fds) == -1 ) {-    errorString_ = "MidiInAlsa::initialize: error creating pipe objects.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Create the input queue-#ifndef AVOID_TIMESTAMPING-  data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue");-  // Set arbitrary tempo (mm=100) and resolution (240)-  snd_seq_queue_tempo_t *qtempo;-  snd_seq_queue_tempo_alloca(&qtempo);-  snd_seq_queue_tempo_set_tempo(qtempo, 600000);-  snd_seq_queue_tempo_set_ppq(qtempo, 240);-  snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo);-  snd_seq_drain_output(data->seq);-#endif-}--// This function is used to count or get the pinfo structure for a given port number.-unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )-{-  snd_seq_client_info_t *cinfo;-  int client;-  int count = 0;-  snd_seq_client_info_alloca( &cinfo );--  snd_seq_client_info_set_client( cinfo, -1 );-  while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {-    client = snd_seq_client_info_get_client( cinfo );-    if ( client == 0 ) continue;-    // Reset query info-    snd_seq_port_info_set_client( pinfo, client );-    snd_seq_port_info_set_port( pinfo, -1 );-    while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {-      unsigned int atyp = snd_seq_port_info_get_type( pinfo );-      if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&-        ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue;-      unsigned int caps = snd_seq_port_info_get_capability( pinfo );-      if ( ( caps & type ) != type ) continue;-      if ( count == portNumber ) return 1;-      ++count;-    }-  }--  // If a negative portNumber was used, return the port count.-  if ( portNumber < 0 ) return count;-  return 0;-}--unsigned int MidiInAlsa :: getPortCount()-{-  snd_seq_port_info_t *pinfo;-  snd_seq_port_info_alloca( &pinfo );--  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );-}--std::string MidiInAlsa :: getPortName( unsigned int portNumber )-{-  snd_seq_client_info_t *cinfo;-  snd_seq_port_info_t *pinfo;-  snd_seq_client_info_alloca( &cinfo );-  snd_seq_port_info_alloca( &pinfo );--  std::string stringName;-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {-    int cnum = snd_seq_port_info_get_client( pinfo );-    snd_seq_get_any_client_info( data->seq, cnum, cinfo );-    std::ostringstream os;-    os << snd_seq_client_info_get_name( cinfo );-    os << " ";                                    // These lines added to make sure devices are listed-    os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names-    os << ":";-    os << snd_seq_port_info_get_port( pinfo );-    stringName = os.str();-    return stringName;-  }--  // If we get here, we didn't find a match.-  errorString_ = "MidiInAlsa::getPortName: error looking for port name!";-  error( RtMidiError::WARNING, errorString_ );-  return stringName;-}--void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName )-{-  if ( connected_ ) {-    errorString_ = "MidiInAlsa::openPort: a valid connection already exists!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  unsigned int nSrc = this->getPortCount();-  if ( nSrc < 1 ) {-    errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!";-    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );-    return;-  }--  snd_seq_port_info_t *src_pinfo;-  snd_seq_port_info_alloca( &src_pinfo );-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {-    std::ostringstream ost;-    ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::INVALID_PARAMETER, errorString_ );-    return;-  }--  snd_seq_addr_t sender, receiver;-  sender.client = snd_seq_port_info_get_client( src_pinfo );-  sender.port = snd_seq_port_info_get_port( src_pinfo );-  receiver.client = snd_seq_client_id( data->seq );--  snd_seq_port_info_t *pinfo;-  snd_seq_port_info_alloca( &pinfo );-  if ( data->vport < 0 ) {-    snd_seq_port_info_set_client( pinfo, 0 );-    snd_seq_port_info_set_port( pinfo, 0 );-    snd_seq_port_info_set_capability( pinfo,-                                      SND_SEQ_PORT_CAP_WRITE |-                                      SND_SEQ_PORT_CAP_SUBS_WRITE );-    snd_seq_port_info_set_type( pinfo,-                                SND_SEQ_PORT_TYPE_MIDI_GENERIC |-                                SND_SEQ_PORT_TYPE_APPLICATION );-    snd_seq_port_info_set_midi_channels(pinfo, 16);-#ifndef AVOID_TIMESTAMPING-    snd_seq_port_info_set_timestamping(pinfo, 1);-    snd_seq_port_info_set_timestamp_real(pinfo, 1);    -    snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);-#endif-    snd_seq_port_info_set_name(pinfo,  portName.c_str() );-    data->vport = snd_seq_create_port(data->seq, pinfo);-  -    if ( data->vport < 0 ) {-      errorString_ = "MidiInAlsa::openPort: ALSA error creating input port.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }-    data->vport = snd_seq_port_info_get_port(pinfo);-  }--  receiver.port = data->vport;--  if ( !data->subscription ) {-    // Make subscription-    if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {-      errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }-    snd_seq_port_subscribe_set_sender(data->subscription, &sender);-    snd_seq_port_subscribe_set_dest(data->subscription, &receiver);-    if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {-      snd_seq_port_subscribe_free( data->subscription );-      data->subscription = 0;-      errorString_ = "MidiInAlsa::openPort: ALSA error making port connection.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }-  }--  if ( inputData_.doInput == false ) {-    // Start the input queue-#ifndef AVOID_TIMESTAMPING-    snd_seq_start_queue( data->seq, data->queue_id, NULL );-    snd_seq_drain_output( data->seq );-#endif-    // Start our MIDI input thread.-    pthread_attr_t attr;-    pthread_attr_init(&attr);-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);-    pthread_attr_setschedpolicy(&attr, SCHED_OTHER);--    inputData_.doInput = true;-    int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);-    pthread_attr_destroy(&attr);-    if ( err ) {-      snd_seq_unsubscribe_port( data->seq, data->subscription );-      snd_seq_port_subscribe_free( data->subscription );-      data->subscription = 0;-      inputData_.doInput = false;-      errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";-      error( RtMidiError::THREAD_ERROR, errorString_ );-      return;-    }-  }--  connected_ = true;-}--void MidiInAlsa :: openVirtualPort( std::string portName )-{-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( data->vport < 0 ) {-    snd_seq_port_info_t *pinfo;-    snd_seq_port_info_alloca( &pinfo );-    snd_seq_port_info_set_capability( pinfo,-				      SND_SEQ_PORT_CAP_WRITE |-				      SND_SEQ_PORT_CAP_SUBS_WRITE );-    snd_seq_port_info_set_type( pinfo,-				SND_SEQ_PORT_TYPE_MIDI_GENERIC |-				SND_SEQ_PORT_TYPE_APPLICATION );-    snd_seq_port_info_set_midi_channels(pinfo, 16);-#ifndef AVOID_TIMESTAMPING-    snd_seq_port_info_set_timestamping(pinfo, 1);-    snd_seq_port_info_set_timestamp_real(pinfo, 1);    -    snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);-#endif-    snd_seq_port_info_set_name(pinfo, portName.c_str());-    data->vport = snd_seq_create_port(data->seq, pinfo);--    if ( data->vport < 0 ) {-      errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }-    data->vport = snd_seq_port_info_get_port(pinfo);-  }--  if ( inputData_.doInput == false ) {-    // Wait for old thread to stop, if still running-    if ( !pthread_equal(data->thread, data->dummy_thread_id) )-      pthread_join( data->thread, NULL );--    // Start the input queue-#ifndef AVOID_TIMESTAMPING-    snd_seq_start_queue( data->seq, data->queue_id, NULL );-    snd_seq_drain_output( data->seq );-#endif-    // Start our MIDI input thread.-    pthread_attr_t attr;-    pthread_attr_init(&attr);-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);-    pthread_attr_setschedpolicy(&attr, SCHED_OTHER);--    inputData_.doInput = true;-    int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);-    pthread_attr_destroy(&attr);-    if ( err ) {-      if ( data->subscription ) {-        snd_seq_unsubscribe_port( data->seq, data->subscription );-        snd_seq_port_subscribe_free( data->subscription );-        data->subscription = 0;-      }-      inputData_.doInput = false;-      errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";-      error( RtMidiError::THREAD_ERROR, errorString_ );-      return;-    }-  }-}--void MidiInAlsa :: closePort( void )-{-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);--  if ( connected_ ) {-    if ( data->subscription ) {-      snd_seq_unsubscribe_port( data->seq, data->subscription );-      snd_seq_port_subscribe_free( data->subscription );-      data->subscription = 0;-    }-    // Stop the input queue-#ifndef AVOID_TIMESTAMPING-    snd_seq_stop_queue( data->seq, data->queue_id, NULL );-    snd_seq_drain_output( data->seq );-#endif-    connected_ = false;-  }--  // Stop thread to avoid triggering the callback, while the port is intended to be closed-  if ( inputData_.doInput ) {-    inputData_.doInput = false;-    int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );-    (void) res;-    if ( !pthread_equal(data->thread, data->dummy_thread_id) )-      pthread_join( data->thread, NULL );-  }-}--//*********************************************************************//-//  API: LINUX ALSA-//  Class Definitions: MidiOutAlsa-//*********************************************************************//--MidiOutAlsa :: MidiOutAlsa( const std::string clientName ) : MidiOutApi()-{-  initialize( clientName );-}--MidiOutAlsa :: ~MidiOutAlsa()-{-  // Close a connection if it exists.-  closePort();--  // Cleanup.-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );-  if ( data->coder ) snd_midi_event_free( data->coder );-  if ( data->buffer ) free( data->buffer );-  snd_seq_close( data->seq );-  delete data;-}--void MidiOutAlsa :: initialize( const std::string& clientName )-{-  // Set up the ALSA sequencer client.-  snd_seq_t *seq;-  int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );-  if ( result1 < 0 ) {-    errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-	}--  // Set client name.-  snd_seq_set_client_name( seq, clientName.c_str() );--  // Save our api-specific connection information.-  AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;-  data->seq = seq;-  data->portNum = -1;-  data->vport = -1;-  data->bufferSize = 32;-  data->coder = 0;-  data->buffer = 0;-  int result = snd_midi_event_new( data->bufferSize, &data->coder );-  if ( result < 0 ) {-    delete data;-    errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }-  data->buffer = (unsigned char *) malloc( data->bufferSize );-  if ( data->buffer == NULL ) {-    delete data;-    errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";-    error( RtMidiError::MEMORY_ERROR, errorString_ );-    return;-  }-  snd_midi_event_init( data->coder );-  apiData_ = (void *) data;-}--unsigned int MidiOutAlsa :: getPortCount()-{-	snd_seq_port_info_t *pinfo;-	snd_seq_port_info_alloca( &pinfo );--  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );-}--std::string MidiOutAlsa :: getPortName( unsigned int portNumber )-{-  snd_seq_client_info_t *cinfo;-  snd_seq_port_info_t *pinfo;-  snd_seq_client_info_alloca( &cinfo );-  snd_seq_port_info_alloca( &pinfo );--  std::string stringName;-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {-    int cnum = snd_seq_port_info_get_client(pinfo);-    snd_seq_get_any_client_info( data->seq, cnum, cinfo );-    std::ostringstream os;-    os << snd_seq_client_info_get_name(cinfo);-    os << " ";                                    // These lines added to make sure devices are listed-    os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names-    os << ":";-    os << snd_seq_port_info_get_port(pinfo);-    stringName = os.str();-    return stringName;-  }--  // If we get here, we didn't find a match.-  errorString_ = "MidiOutAlsa::getPortName: error looking for port name!";-  error( RtMidiError::WARNING, errorString_ );-  return stringName;-}--void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string portName )-{-  if ( connected_ ) {-    errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  unsigned int nSrc = this->getPortCount();-  if (nSrc < 1) {-    errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!";-    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );-    return;-  }--	snd_seq_port_info_t *pinfo;-	snd_seq_port_info_alloca( &pinfo );-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {-    std::ostringstream ost;-    ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::INVALID_PARAMETER, errorString_ );-    return;-  }--  snd_seq_addr_t sender, receiver;-  receiver.client = snd_seq_port_info_get_client( pinfo );-  receiver.port = snd_seq_port_info_get_port( pinfo );-  sender.client = snd_seq_client_id( data->seq );--  if ( data->vport < 0 ) {-    data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),-                                              SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,-                                              SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );-    if ( data->vport < 0 ) {-      errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }-  }--  sender.port = data->vport;--  // Make subscription-  if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {-    snd_seq_port_subscribe_free( data->subscription );-    errorString_ = "MidiOutAlsa::openPort: error allocating port subscription.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }-  snd_seq_port_subscribe_set_sender(data->subscription, &sender);-  snd_seq_port_subscribe_set_dest(data->subscription, &receiver);-  snd_seq_port_subscribe_set_time_update(data->subscription, 1);-  snd_seq_port_subscribe_set_time_real(data->subscription, 1);-  if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {-    snd_seq_port_subscribe_free( data->subscription );-    errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  connected_ = true;-}--void MidiOutAlsa :: closePort( void )-{-  if ( connected_ ) {-    AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-    snd_seq_unsubscribe_port( data->seq, data->subscription );-    snd_seq_port_subscribe_free( data->subscription );-    connected_ = false;-  }-}--void MidiOutAlsa :: openVirtualPort( std::string portName )-{-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  if ( data->vport < 0 ) {-    data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),-                                              SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,-                                              SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );--    if ( data->vport < 0 ) {-      errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-    }-  }-}--void MidiOutAlsa :: sendMessage( std::vector<unsigned char> *message )-{-  int result;-  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);-  unsigned int nBytes = message->size();-  if ( nBytes > data->bufferSize ) {-    data->bufferSize = nBytes;-    result = snd_midi_event_resize_buffer ( data->coder, nBytes);-    if ( result != 0 ) {-      errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }-    free (data->buffer);-    data->buffer = (unsigned char *) malloc( data->bufferSize );-    if ( data->buffer == NULL ) {-    errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";-    error( RtMidiError::MEMORY_ERROR, errorString_ );-    return;-    }-  }--  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->at(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;-  }-  snd_seq_drain_output(data->seq);-}--#endif // __LINUX_ALSA__---//*********************************************************************//-//  API: Windows Multimedia Library (MM)-//*********************************************************************//--// API information deciphered from:-//  - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp--// Thanks to Jean-Baptiste Berruchon for the sysex code.--#if defined(__WINDOWS_MM__)--// The Windows MM API is based on the use of a callback function for-// MIDI input.  We convert the system specific time stamps to delta-// time values.--// Windows MM MIDI header files.-#include <windows.h>-#include <mmsystem.h>--#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 {-  HMIDIIN inHandle;    // Handle to Midi Input Device-  HMIDIOUT outHandle;  // Handle to Midi Output Device-  DWORD lastTime;-  MidiInApi::MidiMessage message;-  LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];-  CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo-};--//*********************************************************************//-//  API: Windows MM-//  Class Definitions: MidiInWinMM-//*********************************************************************//--static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/,-                                        UINT inputStatus, -                                        DWORD_PTR instancePtr,-                                        DWORD_PTR midiMessage,-                                        DWORD timestamp )-{-  if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;--  //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr);-  MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr;-  WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);--  // Calculate time stamp.-  if ( data->firstMessage == true ) {-    apiData->message.timeStamp = 0.0;-    data->firstMessage = false;-  }-  else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;-  apiData->lastTime = timestamp;--  if ( inputStatus == MIM_DATA ) { // Channel or system message--    // Make sure the first byte is a status byte.-    unsigned char status = (unsigned char) (midiMessage & 0x000000FF);-    if ( !(status & 0x80) ) return;--    // Determine the number of bytes in the MIDI message.-    unsigned short nBytes = 1;-    if ( status < 0xC0 ) nBytes = 3;-    else if ( status < 0xE0 ) nBytes = 2;-    else if ( status < 0xF0 ) nBytes = 3;-    else if ( status == 0xF1 ) {-      if ( data->ignoreFlags & 0x02 ) return;-      else nBytes = 2;-    }-    else if ( status == 0xF2 ) nBytes = 3;-    else if ( status == 0xF3 ) nBytes = 2;-    else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {-      // A MIDI timing tick message and we're ignoring it.-      return;-    }-    else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {-      // A MIDI active sensing message and we're ignoring it.-      return;-    }--    // Copy bytes to our MIDI message.-    unsigned char *ptr = (unsigned char *) &midiMessage;-    for ( int i=0; i<nBytes; ++i ) apiData->message.bytes.push_back( *ptr++ );-  }-  else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )-    MIDIHDR *sysex = ( MIDIHDR *) midiMessage; -    if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) {  -      // Sysex message and we're not ignoring it-      for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i )-        apiData->message.bytes.push_back( sysex->lpData[i] );-    }--    // The WinMM API requires that the sysex buffer be requeued after-    // input of each sysex message.  Even if we are ignoring sysex-    // messages, we still need to requeue the buffer in case the user-    // decides to not ignore sysex messages in the future.  However,-    // it seems that WinMM calls this function with an empty sysex-    // buffer when an application closes and in this case, we should-    // avoid requeueing it, else the computer suddenly reboots after-    // one or two minutes.-    if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {-      //if ( sysex->dwBytesRecorded > 0 ) {-      EnterCriticalSection( &(apiData->_mutex) );-      MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );-      LeaveCriticalSection( &(apiData->_mutex) );-      if ( result != MMSYSERR_NOERROR )-        std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";--      if ( data->ignoreFlags & 0x01 ) return;-    }-    else return;-  }--  if ( data->usingCallback ) {-    RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;-    callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );-  }-  else {-    // As long as we haven't reached our queue size limit, push the message.-    if ( data->queue.size < data->queue.ringSize ) {-      data->queue.ring[data->queue.back++] = apiData->message;-      if ( data->queue.back == data->queue.ringSize )-        data->queue.back = 0;-      data->queue.size++;-    }-    else-      std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";-  }--  // Clear the vector for the next input message.-  apiData->message.bytes.clear();-}--MidiInWinMM :: MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )-{-  initialize( clientName );-}--MidiInWinMM :: ~MidiInWinMM()-{-  // Close a connection if it exists.-  closePort();--  WinMidiData *data = static_cast<WinMidiData *> (apiData_);-  DeleteCriticalSection( &(data->_mutex) );--  // Cleanup.-  delete data;-}--void MidiInWinMM :: initialize( const std::string& /*clientName*/ )-{-  // We'll issue a warning here if no devices are available but not-  // throw an error since the user can plugin something later.-  unsigned int nDevices = midiInGetNumDevs();-  if ( nDevices == 0 ) {-    errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available.";-    error( RtMidiError::WARNING, errorString_ );-  }--  // Save our api-specific connection information.-  WinMidiData *data = (WinMidiData *) new WinMidiData;-  apiData_ = (void *) data;-  inputData_.apiData = (void *) data;-  data->message.bytes.clear();  // needs to be empty for first input message--  if ( !InitializeCriticalSectionAndSpinCount(&(data->_mutex), 0x00000400) ) {-    errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed.";-    error( RtMidiError::WARNING, errorString_ );-  }-}--void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )-{-  if ( connected_ ) {-    errorString_ = "MidiInWinMM::openPort: a valid connection already exists!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  unsigned int nDevices = midiInGetNumDevs();-  if (nDevices == 0) {-    errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!";-    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );-    return;-  }--  if ( portNumber >= nDevices ) {-    std::ostringstream ost;-    ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::INVALID_PARAMETER, errorString_ );-    return;-  }--  WinMidiData *data = static_cast<WinMidiData *> (apiData_);-  MMRESULT result = midiInOpen( &data->inHandle,-                                portNumber,-                                (DWORD_PTR)&midiInputCallback,-                                (DWORD_PTR)&inputData_,-                                CALLBACK_FUNCTION );-  if ( result != MMSYSERR_NOERROR ) {-    errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Allocate and init the sysex buffers.-  for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++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]->dwUser = i; // We use the dwUser parameter as buffer indicator-    data->sysexBuffer[i]->dwFlags = 0;--    result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );-    if ( result != MMSYSERR_NOERROR ) {-      midiInClose( data->inHandle );-      errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader).";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }--    // Register the buffer.-    result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );-    if ( result != MMSYSERR_NOERROR ) {-      midiInClose( data->inHandle );-      errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer).";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }-  }--  result = midiInStart( data->inHandle );-  if ( result != MMSYSERR_NOERROR ) {-    midiInClose( data->inHandle );-    errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  connected_ = true;-}--void MidiInWinMM :: openVirtualPort( std::string /*portName*/ )-{-  // This function cannot be implemented for the Windows MM MIDI API.-  errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";-  error( RtMidiError::WARNING, errorString_ );-}--void MidiInWinMM :: closePort( void )-{-  if ( connected_ ) {-    WinMidiData *data = static_cast<WinMidiData *> (apiData_);-    EnterCriticalSection( &(data->_mutex) );-    midiInReset( data->inHandle );-    midiInStop( data->inHandle );--    for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {-      int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));-      delete [] data->sysexBuffer[i]->lpData;-      delete [] data->sysexBuffer[i];-      if ( result != MMSYSERR_NOERROR ) {-        midiInClose( data->inHandle );-        errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";-        error( RtMidiError::DRIVER_ERROR, errorString_ );-        return;-      }-    }--    midiInClose( data->inHandle );-    connected_ = false;-    LeaveCriticalSection( &(data->_mutex) );-  }-}--unsigned int MidiInWinMM :: getPortCount()-{-  return midiInGetNumDevs();-}--std::string MidiInWinMM :: getPortName( unsigned int portNumber )-{-  std::string stringName;-  unsigned int nDevices = midiInGetNumDevs();-  if ( portNumber >= nDevices ) {-    std::ostringstream ost;-    ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::WARNING, errorString_ );-    return stringName;-  }--  MIDIINCAPS deviceCaps;-  midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));--#if defined( UNICODE ) || defined( _UNICODE )-  int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;-  stringName.assign( length, 0 );-  length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);-#else-  stringName = std::string( deviceCaps.szPname );-#endif--  // Next lines added to add the portNumber to the name so that -  // the device's names are sure to be listed with individual names-  // even when they have the same brand name-  std::ostringstream os;-  os << " ";-  os << portNumber;-  stringName += os.str();--  return stringName;-}--//*********************************************************************//-//  API: Windows MM-//  Class Definitions: MidiOutWinMM-//*********************************************************************//--MidiOutWinMM :: MidiOutWinMM( const std::string clientName ) : MidiOutApi()-{-  initialize( clientName );-}--MidiOutWinMM :: ~MidiOutWinMM()-{-  // Close a connection if it exists.-  closePort();--  // Cleanup.-  WinMidiData *data = static_cast<WinMidiData *> (apiData_);-  delete data;-}--void MidiOutWinMM :: initialize( const std::string& /*clientName*/ )-{-  // We'll issue a warning here if no devices are available but not-  // throw an error since the user can plug something in later.-  unsigned int nDevices = midiOutGetNumDevs();-  if ( nDevices == 0 ) {-    errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available.";-    error( RtMidiError::WARNING, errorString_ );-  }--  // Save our api-specific connection information.-  WinMidiData *data = (WinMidiData *) new WinMidiData;-  apiData_ = (void *) data;-}--unsigned int MidiOutWinMM :: getPortCount()-{-  return midiOutGetNumDevs();-}--std::string MidiOutWinMM :: getPortName( unsigned int portNumber )-{-  std::string stringName;-  unsigned int nDevices = midiOutGetNumDevs();-  if ( portNumber >= nDevices ) {-    std::ostringstream ost;-    ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::WARNING, errorString_ );-    return stringName;-  }--  MIDIOUTCAPS deviceCaps;-  midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));--#if defined( UNICODE ) || defined( _UNICODE )-  int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;-  stringName.assign( length, 0 );-  length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);-#else-  stringName = std::string( deviceCaps.szPname );-#endif--  // Next lines added to add the portNumber to the name so that -  // the device's names are sure to be listed with individual names-  // even when they have the same brand name-  std::ostringstream os;-  os << " ";-  os << portNumber;-  stringName += os.str();--  return stringName;-}--void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )-{-  if ( connected_ ) {-    errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  unsigned int nDevices = midiOutGetNumDevs();-  if (nDevices < 1) {-    errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!";-    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );-    return;-  }--  if ( portNumber >= nDevices ) {-    std::ostringstream ost;-    ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::INVALID_PARAMETER, errorString_ );-    return;-  }--  WinMidiData *data = static_cast<WinMidiData *> (apiData_);-  MMRESULT result = midiOutOpen( &data->outHandle,-                                 portNumber,-                                 (DWORD)NULL,-                                 (DWORD)NULL,-                                 CALLBACK_NULL );-  if ( result != MMSYSERR_NOERROR ) {-    errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port.";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  connected_ = true;-}--void MidiOutWinMM :: closePort( void )-{-  if ( connected_ ) {-    WinMidiData *data = static_cast<WinMidiData *> (apiData_);-    midiOutReset( data->outHandle );-    midiOutClose( data->outHandle );-    connected_ = false;-  }-}--void MidiOutWinMM :: openVirtualPort( std::string /*portName*/ )-{-  // This function cannot be implemented for the Windows MM MIDI API.-  errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";-  error( RtMidiError::WARNING, errorString_ );-}--void MidiOutWinMM :: sendMessage( std::vector<unsigned char> *message )-{-  if ( !connected_ ) return;--  unsigned int nBytes = static_cast<unsigned int>(message->size());-  if ( nBytes == 0 ) {-    errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  MMRESULT result;-  WinMidiData *data = static_cast<WinMidiData *> (apiData_);-  if ( message->at(0) == 0xF0 ) { // Sysex message--    // Allocate buffer for sysex data.-    char *buffer = (char *) malloc( nBytes );-    if ( buffer == NULL ) {-      errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!";-      error( RtMidiError::MEMORY_ERROR, errorString_ );-      return;-    }--    // Copy data to buffer.-    for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message->at(i);--    // Create and prepare MIDIHDR structure.-    MIDIHDR sysex;-    sysex.lpData = (LPSTR) buffer;-    sysex.dwBufferLength = nBytes;-    sysex.dwFlags = 0;-    result = midiOutPrepareHeader( data->outHandle,  &sysex, sizeof(MIDIHDR) ); -    if ( result != MMSYSERR_NOERROR ) {-      free( buffer );-      errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }--    // Send the message.-    result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) );-    if ( result != MMSYSERR_NOERROR ) {-      free( buffer );-      errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-      return;-    }--    // Unprepare the buffer and MIDIHDR.-    while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 );-    free( buffer );-  }-  else { // Channel or system message.--    // Make sure the message size isn't too big.-    if ( nBytes > 3 ) {-      errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!";-      error( RtMidiError::WARNING, errorString_ );-      return;-    }--    // Pack MIDI bytes into double word.-    DWORD packet;-    unsigned char *ptr = (unsigned char *) &packet;-    for ( unsigned int i=0; i<nBytes; ++i ) {-      *ptr = message->at(i);-      ++ptr;-    }--    // Send the message immediately.-    result = midiOutShortMsg( data->outHandle, packet );-    if ( result != MMSYSERR_NOERROR ) {-      errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message.";-      error( RtMidiError::DRIVER_ERROR, errorString_ );-    }-  }-}--#endif  // __WINDOWS_MM__---//*********************************************************************//-//  API: UNIX JACK-//-//  Written primarily by Alexander Svetalkin, with updates for delta-//  time by Gary Scavone, April 2011.-//-//  *********************************************************************//--#if defined(__UNIX_JACK__)--// JACK header files-#include <jack/jack.h>-#include <jack/midiport.h>-#include <jack/ringbuffer.h>--#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer--struct JackMidiData {-  jack_client_t *client;-  jack_port_t *port;-  jack_ringbuffer_t *buffSize;-  jack_ringbuffer_t *buffMessage;-  jack_time_t lastTime;-  MidiInApi :: RtMidiInData *rtMidiIn;-  };--//*********************************************************************//-//  API: JACK-//  Class Definitions: MidiInJack-//*********************************************************************//--static int jackProcessIn( jack_nframes_t nframes, void *arg )-{-  JackMidiData *jData = (JackMidiData *) arg;-  MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn;-  jack_midi_event_t event;-  jack_time_t time;--  // Is port created?-  if ( jData->port == NULL ) return 0;-  void *buff = jack_port_get_buffer( jData->port, nframes );--  // We have midi events in buffer-  int evCount = jack_midi_get_event_count( buff );-  for (int j = 0; j < evCount; j++) {-    MidiInApi::MidiMessage message;-    message.bytes.clear();--    jack_midi_event_get( &event, buff, j );--    for ( unsigned int i = 0; i < event.size; i++ )-      message.bytes.push_back( event.buffer[i] );--    // Compute the delta time.-    time = jack_get_time();-    if ( rtData->firstMessage == true )-      rtData->firstMessage = false;-    else-      message.timeStamp = ( time - jData->lastTime ) * 0.000001;--    jData->lastTime = time;--    if ( !rtData->continueSysex ) {-      if ( rtData->usingCallback ) {-        RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;-        callback( message.timeStamp, &message.bytes, rtData->userData );-      }-      else {-        // As long as we haven't reached our queue size limit, push the message.-        if ( rtData->queue.size < rtData->queue.ringSize ) {-          rtData->queue.ring[rtData->queue.back++] = message;-          if ( rtData->queue.back == rtData->queue.ringSize )-            rtData->queue.back = 0;-          rtData->queue.size++;-        }-        else-          std::cerr << "\nMidiInJack: message queue limit reached!!\n\n";-      }-    }-  }--  return 0;-}--MidiInJack :: MidiInJack( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )-{-  initialize( clientName );-}--void MidiInJack :: initialize( const std::string& clientName )-{-  JackMidiData *data = new JackMidiData;-  apiData_ = (void *) data;--  data->rtMidiIn = &inputData_;-  data->port = NULL;-  data->client = NULL;-  this->clientName = clientName;--  connect();-}--void MidiInJack :: connect()-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  if ( data->client )-    return;--  // Initialize JACK client-  if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {-    errorString_ = "MidiInJack::initialize: JACK server not running?";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  jack_set_process_callback( data->client, jackProcessIn, data );-  jack_activate( data->client );-}--MidiInJack :: ~MidiInJack()-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  closePort();--  if ( data->client )-    jack_client_close( data->client );-  delete data;-}--void MidiInJack :: openPort( unsigned int portNumber, const std::string portName )-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);--  connect();--  // Creating new port-  if ( data->port == NULL)-    data->port = jack_port_register( data->client, portName.c_str(),-                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );--  if ( data->port == NULL) {-    errorString_ = "MidiInJack::openPort: JACK error creating port";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Connecting to the output-  std::string name = getPortName( portNumber );-  jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );-}--void MidiInJack :: openVirtualPort( const std::string portName )-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);--  connect();-  if ( data->port == NULL )-    data->port = jack_port_register( data->client, portName.c_str(),-                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );--  if ( data->port == NULL ) {-    errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-  }-}--unsigned int MidiInJack :: getPortCount()-{-  int count = 0;-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  connect();-  if ( !data->client )-    return 0;--  // List of available ports-  const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );--  if ( ports == NULL ) return 0;-  while ( ports[count] != NULL )-    count++;--  free( ports );--  return count;-}--std::string MidiInJack :: getPortName( unsigned int portNumber )-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  std::string retStr("");--  connect();--  // List of available ports-  const char **ports = jack_get_ports( data->client, NULL,-                                       JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );--  // Check port validity-  if ( ports == NULL ) {-    errorString_ = "MidiInJack::getPortName: no ports available!";-    error( RtMidiError::WARNING, errorString_ );-    return retStr;-  }--  if ( ports[portNumber] == NULL ) {-    std::ostringstream ost;-    ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::WARNING, errorString_ );-  }-  else retStr.assign( ports[portNumber] );--  free( ports );-  return retStr;-}--void MidiInJack :: closePort()-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);--  if ( data->port == NULL ) return;-  jack_port_unregister( data->client, data->port );-  data->port = NULL;-}--//*********************************************************************//-//  API: JACK-//  Class Definitions: MidiOutJack-//*********************************************************************//--// Jack process callback-static int jackProcessOut( jack_nframes_t nframes, void *arg )-{-  JackMidiData *data = (JackMidiData *) arg;-  jack_midi_data_t *midiData;-  int space;--  // Is port created?-  if ( data->port == NULL ) return 0;--  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 );--    jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );-  }--  return 0;-}--MidiOutJack :: MidiOutJack( const std::string clientName ) : MidiOutApi()-{-  initialize( clientName );-}--void MidiOutJack :: initialize( const std::string& clientName )-{-  JackMidiData *data = new JackMidiData;-  apiData_ = (void *) data;--  data->port = NULL;-  data->client = NULL;-  this->clientName = clientName;--  connect();-}--void MidiOutJack :: connect()-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  if ( data->client )-    return;-  -  // Initialize output ringbuffers  -  data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );-  data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );--  // Initialize JACK client-  if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {-    errorString_ = "MidiOutJack::initialize: JACK server not running?";-    error( RtMidiError::WARNING, errorString_ );-    return;-  }--  jack_set_process_callback( data->client, jackProcessOut, data );-  jack_activate( data->client );-}--MidiOutJack :: ~MidiOutJack()-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  closePort();-  -  // Cleanup-  jack_ringbuffer_free( data->buffSize );-  jack_ringbuffer_free( data->buffMessage );-  if ( data->client ) {-    jack_client_close( data->client );-  }--  delete data;-}--void MidiOutJack :: openPort( unsigned int portNumber, const std::string portName )-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);--  connect();--  // Creating new port-  if ( data->port == NULL )-    data->port = jack_port_register( data->client, portName.c_str(),-      JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );--  if ( data->port == NULL ) {-    errorString_ = "MidiOutJack::openPort: JACK error creating port";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-    return;-  }--  // Connecting to the output-  std::string name = getPortName( portNumber );-  jack_connect( data->client, jack_port_name( data->port ), name.c_str() );-}--void MidiOutJack :: openVirtualPort( const std::string portName )-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);--  connect();-  if ( data->port == NULL )-    data->port = jack_port_register( data->client, portName.c_str(),-      JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );--  if ( data->port == NULL ) {-    errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";-    error( RtMidiError::DRIVER_ERROR, errorString_ );-  }-}--unsigned int MidiOutJack :: getPortCount()-{-  int count = 0;-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  connect();-  if ( !data->client )-    return 0;--  // List of available ports-  const char **ports = jack_get_ports( data->client, NULL,-    JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );--  if ( ports == NULL ) return 0;-  while ( ports[count] != NULL )-    count++;--  free( ports );--  return count;-}--std::string MidiOutJack :: getPortName( unsigned int portNumber )-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);-  std::string retStr("");--  connect();--  // List of available ports-  const char **ports = jack_get_ports( data->client, NULL,-    JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );--  // Check port validity-  if ( ports == NULL) {-    errorString_ = "MidiOutJack::getPortName: no ports available!";-    error( RtMidiError::WARNING, errorString_ );-    return retStr;-  }--  if ( ports[portNumber] == NULL) {-    std::ostringstream ost;-    ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";-    errorString_ = ost.str();-    error( RtMidiError::WARNING, errorString_ );-  }-  else retStr.assign( ports[portNumber] );--  free( ports );-  return retStr;-}--void MidiOutJack :: closePort()-{-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);--  if ( data->port == NULL ) return;-  jack_port_unregister( data->client, data->port );-  data->port = NULL;-}--void MidiOutJack :: sendMessage( std::vector<unsigned char> *message )-{-  int nBytes = message->size();-  JackMidiData *data = static_cast<JackMidiData *> (apiData_);--  // Write full message to buffer-  jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0],-                         message->size() );+    RtMidi GitHub site: https://github.com/thestk/rtmidi+    RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/++    RtMidi: realtime MIDI i/o C++ classes+    Copyright (c) 2003-2019 Gary P. Scavone++    Permission is hereby granted, free of charge, to any person+    obtaining a copy of this software and associated documentation files+    (the "Software"), to deal in the Software without restriction,+    including without limitation the rights to use, copy, modify, merge,+    publish, distribute, sublicense, and/or sell copies of the Software,+    and to permit persons to whom the Software is furnished to do so,+    subject to the following conditions:++    The above copyright notice and this permission notice shall be+    included in all copies or substantial portions of the Software.++    Any person wishing to distribute modifications to the Software is+    asked to send the modifications to the original developer so that+    they can be incorporated into the canonical version.  This is,+    however, not a binding provision of this license.++    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.+*/+/**********************************************************************/++#include "RtMidi.h"+#include <sstream>++#if defined(__MACOSX_CORE__)+  #if TARGET_OS_IPHONE+    #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime+    #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos+  #endif+#endif++// 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 RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES++// **************************************************************** //+//+// MidiInApi and MidiOutApi subclass prototypes.+//+// **************************************************************** //++#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__)+  #define __RTMIDI_DUMMY__+#endif++#if defined(__MACOSX_CORE__)++class MidiInCore: public MidiInApi+{+ public:+  MidiInCore( const std::string &clientName, unsigned int queueSizeLimit );+  ~MidiInCore( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };+  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 );++ protected:+  void initialize( const std::string& clientName );+};++class MidiOutCore: public MidiOutApi+{+ public:+  MidiOutCore( const std::string &clientName );+  ~MidiOutCore( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };+  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(__UNIX_JACK__)++class MidiInJack: public MidiInApi+{+ public:+  MidiInJack( const std::string &clientName, unsigned int queueSizeLimit );+  ~MidiInJack( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };+  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 );++ protected:+  std::string clientName;++  void connect( void );+  void initialize( const std::string& clientName );+};++class MidiOutJack: public MidiOutApi+{+ public:+  MidiOutJack( const std::string &clientName );+  ~MidiOutJack( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };+  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:+  std::string clientName;++  void connect( void );+  void initialize( const std::string& clientName );+};++#endif++#if defined(__LINUX_ALSA__)++class MidiInAlsa: public MidiInApi+{+ public:+  MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit );+  ~MidiInAlsa( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };+  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 );++ protected:+  void initialize( const std::string& clientName );+};++class MidiOutAlsa: public MidiOutApi+{+ public:+  MidiOutAlsa( const std::string &clientName );+  ~MidiOutAlsa( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };+  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(__WINDOWS_MM__)++class MidiInWinMM: public MidiInApi+{+ public:+  MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit );+  ~MidiInWinMM( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };+  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 );++ protected:+  void initialize( const std::string& clientName );+};++class MidiOutWinMM: public MidiOutApi+{+ public:+  MidiOutWinMM( const std::string &clientName );+  ~MidiOutWinMM( void );+  RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };+  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+{+ public:+ MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }+  RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }+  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 ) { return 0; }+  std::string getPortName( unsigned int /*portNumber*/ ) { return ""; }++ protected:+  void initialize( const std::string& /*clientName*/ ) {}+};++class MidiOutDummy: public MidiOutApi+{+ public:+  MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }+  RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }+  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 ) { return 0; }+  std::string getPortName( unsigned int /*portNumber*/ ) { return ""; }+  void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {}++ protected:+  void initialize( const std::string& /*clientName*/ ) {}+};++#endif++//*********************************************************************//+//  RtMidi Definitions+//*********************************************************************//++RtMidi :: RtMidi()+  : rtapi_(0)+{+}++RtMidi :: ~RtMidi()+{+  delete rtapi_;+  rtapi_ = 0;+}++std::string RtMidi :: getVersion( void ) throw()+{+  return std::string( RTMIDI_VERSION );+}++// Define API names and display names.+// Must be in same order as API enum.+extern "C" {+const char* rtmidi_api_names[][2] = {+  { "unspecified" , "Unknown" },+  { "core"        , "CoreMidi" },+  { "alsa"        , "ALSA" },+  { "jack"        , "Jack" },+  { "winmm"       , "Windows MultiMedia" },+  { "dummy"       , "Dummy" },+};+const unsigned int rtmidi_num_api_names =+  sizeof(rtmidi_api_names)/sizeof(rtmidi_api_names[0]);++// The order here will control the order of RtMidi's API search in+// the constructor.+extern "C" const RtMidi::Api rtmidi_compiled_apis[] = {+#if defined(__MACOSX_CORE__)+  RtMidi::MACOSX_CORE,+#endif+#if defined(__LINUX_ALSA__)+  RtMidi::LINUX_ALSA,+#endif+#if defined(__UNIX_JACK__)+  RtMidi::UNIX_JACK,+#endif+#if defined(__WINDOWS_MM__)+  RtMidi::WINDOWS_MM,+#endif+#if defined(__RTMIDI_DUMMY__)+  RtMidi::RTMIDI_DUMMY,+#endif+  RtMidi::UNSPECIFIED,+};+extern "C" const unsigned int rtmidi_num_compiled_apis =+  sizeof(rtmidi_compiled_apis)/sizeof(rtmidi_compiled_apis[0])-1;+}++// This is a compile-time check that rtmidi_num_api_names == RtMidi::NUM_APIS.+// If the build breaks here, check that they match.+template<bool b> class StaticAssert { private: StaticAssert() {} };+template<> class StaticAssert<true>{ public: StaticAssert() {} };+class StaticAssertions { StaticAssertions() {+  StaticAssert<rtmidi_num_api_names == RtMidi::NUM_APIS>();+}};++void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()+{+  apis = std::vector<RtMidi::Api>(rtmidi_compiled_apis,+                                  rtmidi_compiled_apis + rtmidi_num_compiled_apis);+}++std::string RtMidi :: getApiName( RtMidi::Api api )+{+  if (api < 0 || 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)+    return "Unknown";+  return rtmidi_api_names[api][1];+}++RtMidi::Api RtMidi :: getCompiledApiByName( const std::string &name )+{+  unsigned int i=0;+  for (i = 0; i < rtmidi_num_compiled_apis; ++i)+    if (name == rtmidi_api_names[rtmidi_compiled_apis[i]][0])+      return rtmidi_compiled_apis[i];+  return RtMidi::UNSPECIFIED;+}++void RtMidi :: setClientName( const std::string &clientName )+{+  rtapi_->setClientName( clientName );+}++void RtMidi :: setPortName( const std::string &portName )+{+  rtapi_->setPortName( portName );+}+++//*********************************************************************//+//  RtMidiIn Definitions+//*********************************************************************//++void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit )+{+  delete rtapi_;+  rtapi_ = 0;++#if defined(__UNIX_JACK__)+  if ( api == UNIX_JACK )+    rtapi_ = new MidiInJack( clientName, queueSizeLimit );+#endif+#if defined(__LINUX_ALSA__)+  if ( api == LINUX_ALSA )+    rtapi_ = new MidiInAlsa( clientName, queueSizeLimit );+#endif+#if defined(__WINDOWS_MM__)+  if ( api == WINDOWS_MM )+    rtapi_ = new MidiInWinMM( clientName, queueSizeLimit );+#endif+#if defined(__MACOSX_CORE__)+  if ( api == MACOSX_CORE )+    rtapi_ = new MidiInCore( clientName, queueSizeLimit );+#endif+#if defined(__RTMIDI_DUMMY__)+  if ( api == RTMIDI_DUMMY )+    rtapi_ = new MidiInDummy( clientName, queueSizeLimit );+#endif+}++RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit )+  : RtMidi()+{+  if ( api != UNSPECIFIED ) {+    // Attempt to open the specified API.+    openMidiApi( api, clientName, queueSizeLimit );+    if ( rtapi_ ) return;++    // No compiled support for specified API value.  Issue a warning+    // and continue as if no API was specified.+    std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl;+  }++  // Iterate through the compiled APIs and return as soon as we find+  // one with at least one port or we reach the end of the list.+  std::vector< RtMidi::Api > apis;+  getCompiledApi( apis );+  for ( unsigned int i=0; i<apis.size(); i++ ) {+    openMidiApi( apis[i], clientName, queueSizeLimit );+    if ( rtapi_ && rtapi_->getPortCount() ) break;+  }++  if ( rtapi_ ) return;++  // It should not be possible to get here because the preprocessor+  // definition __RTMIDI_DUMMY__ is automatically defined if no+  // API-specific definitions are passed to the compiler. But just in+  // case something weird happens, we'll throw an error.+  std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!";+  throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );+}++RtMidiIn :: ~RtMidiIn() throw()+{+}+++//*********************************************************************//+//  RtMidiOut Definitions+//*********************************************************************//++void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName )+{+  delete rtapi_;+  rtapi_ = 0;++#if defined(__UNIX_JACK__)+  if ( api == UNIX_JACK )+    rtapi_ = new MidiOutJack( clientName );+#endif+#if defined(__LINUX_ALSA__)+  if ( api == LINUX_ALSA )+    rtapi_ = new MidiOutAlsa( clientName );+#endif+#if defined(__WINDOWS_MM__)+  if ( api == WINDOWS_MM )+    rtapi_ = new MidiOutWinMM( clientName );+#endif+#if defined(__MACOSX_CORE__)+  if ( api == MACOSX_CORE )+    rtapi_ = new MidiOutCore( clientName );+#endif+#if defined(__RTMIDI_DUMMY__)+  if ( api == RTMIDI_DUMMY )+    rtapi_ = new MidiOutDummy( clientName );+#endif+}++RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName)+{+  if ( api != UNSPECIFIED ) {+    // Attempt to open the specified API.+    openMidiApi( api, clientName );+    if ( rtapi_ ) return;++    // No compiled support for specified API value.  Issue a warning+    // and continue as if no API was specified.+    std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl;+  }++  // Iterate through the compiled APIs and return as soon as we find+  // one with at least one port or we reach the end of the list.+  std::vector< RtMidi::Api > apis;+  getCompiledApi( apis );+  for ( unsigned int i=0; i<apis.size(); i++ ) {+    openMidiApi( apis[i], clientName );+    if ( rtapi_ && rtapi_->getPortCount() ) break;+  }++  if ( rtapi_ ) return;++  // It should not be possible to get here because the preprocessor+  // definition __RTMIDI_DUMMY__ is automatically defined if no+  // API-specific definitions are passed to the compiler. But just in+  // case something weird happens, we'll thrown an error.+  std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!";+  throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );+}++RtMidiOut :: ~RtMidiOut() throw()+{+}++//*********************************************************************//+//  Common MidiApi Definitions+//*********************************************************************//++MidiApi :: MidiApi( void )+  : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0)+{+}++MidiApi :: ~MidiApi( void )+{+}++void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 )+{+    errorCallback_ = errorCallback;+    errorCallbackUserData_ = userData;+}++void MidiApi :: error( RtMidiError::Type type, std::string errorString )+{+  if ( errorCallback_ ) {++    if ( firstErrorOccurred_ )+      return;++    firstErrorOccurred_ = true;+    const std::string errorMessage = errorString;++    errorCallback_( type, errorMessage, errorCallbackUserData_ );+    firstErrorOccurred_ = false;+    return;+  }++  if ( type == RtMidiError::WARNING ) {+    std::cerr << '\n' << errorString << "\n\n";+  }+  else if ( type == RtMidiError::DEBUG_WARNING ) {+#if defined(__RTMIDI_DEBUG__)+    std::cerr << '\n' << errorString << "\n\n";+#endif+  }+  else {+    std::cerr << '\n' << errorString << "\n\n";+    throw RtMidiError( errorString, type );+  }+}++//*********************************************************************//+//  Common MidiInApi Definitions+//*********************************************************************//++MidiInApi :: MidiInApi( unsigned int queueSizeLimit )+  : MidiApi()+{+  // Allocate the MIDI queue.+  inputData_.queue.ringSize = queueSizeLimit;+  if ( inputData_.queue.ringSize > 0 )+    inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ];+}++MidiInApi :: ~MidiInApi( void )+{+  // Delete the MIDI queue.+  if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring;+}++void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData )+{+  if ( inputData_.usingCallback ) {+    errorString_ = "MidiInApi::setCallback: a callback function is already set!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  if ( !callback ) {+    errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  inputData_.userCallback = callback;+  inputData_.userData = userData;+  inputData_.usingCallback = true;+}++void MidiInApi :: cancelCallback()+{+  if ( !inputData_.usingCallback ) {+    errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  inputData_.userCallback = 0;+  inputData_.userData = 0;+  inputData_.usingCallback = false;+}++void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )+{+  inputData_.ignoreFlags = 0;+  if ( midiSysex ) inputData_.ignoreFlags = 0x01;+  if ( midiTime ) inputData_.ignoreFlags |= 0x02;+  if ( midiSense ) inputData_.ignoreFlags |= 0x04;+}++double MidiInApi :: getMessage( std::vector<unsigned char> *message )+{+  message->clear();++  if ( inputData_.usingCallback ) {+    errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";+    error( RtMidiError::WARNING, errorString_ );+    return 0.0;+  }++  double timeStamp;+  if ( !inputData_.queue.pop( message, &timeStamp ) )+    return 0.0;++  return timeStamp;+}++unsigned int MidiInApi::MidiQueue::size( unsigned int *__back,+                                         unsigned int *__front )+{+  // Access back/front members exactly once and make stack copies for+  // size calculation+  unsigned int _back = back, _front = front, _size;+  if ( _back >= _front )+    _size = _back - _front;+  else+    _size = ringSize - _front + _back;++  // Return copies of back/front so no new and unsynchronized accesses+  // to member variables are needed.+  if ( __back ) *__back = _back;+  if ( __front ) *__front = _front;+  return _size;+}++// As long as we haven't reached our queue size limit, push the message.+bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg )+{+  // Local stack copies of front/back+  unsigned int _back, _front, _size;++  // Get back/front indexes exactly once and calculate current size+  _size = size( &_back, &_front );++  if ( _size < ringSize-1 )+  {+    ring[_back] = msg;+    back = (back+1)%ringSize;+    return true;+  }++  return false;+}++bool MidiInApi::MidiQueue::pop( std::vector<unsigned char> *msg, double* timeStamp )+{+  // Local stack copies of front/back+  unsigned int _back, _front, _size;++  // Get back/front indexes exactly once and calculate current size+  _size = size( &_back, &_front );++  if ( _size == 0 )+    return false;++  // Copy queued message to the vector pointer argument and then "pop" it.+  msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() );+  *timeStamp = ring[_front].timeStamp;++  // Update front+  front = (front+1)%ringSize;+  return true;+}++//*********************************************************************//+//  Common MidiOutApi Definitions+//*********************************************************************//++MidiOutApi :: MidiOutApi( void )+  : MidiApi()+{+}++MidiOutApi :: ~MidiOutApi( void )+{+}++// *************************************************** //+//+// OS/API-specific methods.+//+// *************************************************** //++#if defined(__MACOSX_CORE__)++// The CoreMIDI API is based on the use of a callback function for+// 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>++// A structure to hold variables related to the CoreMIDI API+// implementation.+struct CoreMidiData {+  MIDIClientRef client;+  MIDIPortRef port;+  MIDIEndpointRef endpoint;+  MIDIEndpointRef destinationId;+  unsigned long long lastTime;+  MIDISysexSendRequest sysexreq;+};++//*********************************************************************//+//  API: OS-X+//  Class Definitions: MidiInCore+//*********************************************************************//++static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ )+{+  MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef);+  CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);++  unsigned char status;+  unsigned short nBytes, iByte, size;+  unsigned long long time;++  bool& continueSysex = data->continueSysex;+  MidiInApi::MidiMessage& message = data->message;++  const MIDIPacket *packet = &list->packet[0];+  for ( unsigned int i=0; i<list->numPackets; ++i ) {++    // My interpretation of the CoreMIDI documentation: all message+    // types, except sysex, are complete within a packet and there may+    // be several of them in a single packet.  Sysex messages can be+    // broken across multiple packets and PacketLists but are bundled+    // alone within each packet (these packets do not contain other+    // message types).  If sysex messages are split across multiple+    // MIDIPacketLists, they must be handled by multiple calls to this+    // function.++    nBytes = packet->length;+    if ( nBytes == 0 ) {+      packet = MIDIPacketNext( packet );+      continue;+    }++    // Calculate time stamp.+    if ( data->firstMessage ) {+      message.timeStamp = 0.0;+      data->firstMessage = false;+    }+    else {+      time = packet->timeStamp;+      if ( time == 0 ) { // this happens when receiving asynchronous sysex messages+        time = AudioGetCurrentHostTime();+      }+      time -= apiData->lastTime;+      time = AudioConvertHostTimeToNanos( time );+      if ( !continueSysex )+        message.timeStamp = time * 0.000000001;+    }++    // Track whether any non-filtered messages were found in this+    // packet for timestamp calculation+    bool foundNonFiltered = false;++    iByte = 0;+    if ( continueSysex ) {+      // We have a continuing, segmented sysex message.+      if ( !( data->ignoreFlags & 0x01 ) ) {+        // If we're not ignoring sysex messages, copy the entire packet.+        for ( unsigned int j=0; j<nBytes; ++j )+          message.bytes.push_back( packet->data[j] );+      }+      continueSysex = packet->data[nBytes-1] != 0xF7;++      if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) {+        // If not a continuing sysex message, invoke the user callback function or queue the message.+        if ( data->usingCallback ) {+          RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;+          callback( message.timeStamp, &message.bytes, data->userData );+        }+        else {+          // As long as we haven't reached our queue size limit, push the message.+          if ( !data->queue.push( message ) )+            std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";+        }+        message.bytes.clear();+      }+    }+    else {+      while ( iByte < nBytes ) {+        size = 0;+        // We are expecting that the next byte in the packet is a status byte.+        status = packet->data[iByte];+        if ( !(status & 0x80) ) break;+        // Determine the number of bytes in the MIDI message.+        if ( status < 0xC0 ) size = 3;+        else if ( status < 0xE0 ) size = 2;+        else if ( status < 0xF0 ) size = 3;+        else if ( status == 0xF0 ) {+          // A MIDI sysex+          if ( data->ignoreFlags & 0x01 ) {+            size = 0;+            iByte = nBytes;+          }+          else size = nBytes - iByte;+          continueSysex = packet->data[nBytes-1] != 0xF7;+        }+        else if ( status == 0xF1 ) {+          // A MIDI time code message+          if ( data->ignoreFlags & 0x02 ) {+            size = 0;+            iByte += 2;+          }+          else size = 2;+        }+        else if ( status == 0xF2 ) size = 3;+        else if ( status == 0xF3 ) size = 2;+        else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {+          // A MIDI timing tick message and we're ignoring it.+          size = 0;+          iByte += 1;+        }+        else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {+          // A MIDI active sensing message and we're ignoring it.+          size = 0;+          iByte += 1;+        }+        else size = 1;++        // Copy the MIDI data to our vector.+        if ( size ) {+          foundNonFiltered = true;+          message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );+          if ( !continueSysex ) {+            // If not a continuing sysex message, invoke the user callback function or queue the message.+            if ( data->usingCallback ) {+              RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;+              callback( message.timeStamp, &message.bytes, data->userData );+            }+            else {+              // As long as we haven't reached our queue size limit, push the message.+              if ( !data->queue.push( message ) )+                std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";+            }+            message.bytes.clear();+          }+          iByte += size;+        }+      }+    }++    // Save the time of the last non-filtered message+    if ( foundNonFiltered ) {+      apiData->lastTime = packet->timeStamp;+      if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages+        apiData->lastTime = AudioGetCurrentHostTime();+      }+    }++    packet = MIDIPacketNext(packet);+  }+}++MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit )+  : MidiInApi( queueSizeLimit )+{+  MidiInCore::initialize( clientName );+}++MidiInCore :: ~MidiInCore( void )+{+  // Close a connection if it exists.+  MidiInCore::closePort();++  // Cleanup.+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);+  MIDIClientDispose( data->client );+  if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );+  delete data;+}++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;+  }++  // Save our api-specific connection information.+  CoreMidiData *data = (CoreMidiData *) new CoreMidiData;+  data->client = client;+  data->endpoint = 0;+  apiData_ = (void *) data;+  inputData_.apiData = (void *) data;+  CFRelease( name );+}++void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName )+{+  if ( connected_ ) {+    errorString_ = "MidiInCore::openPort: a valid connection already exists!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );+  unsigned int nSrc = MIDIGetNumberOfSources();+  if ( nSrc < 1 ) {+    errorString_ = "MidiInCore::openPort: no MIDI input sources found!";+    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );+    return;+  }++  if ( portNumber >= nSrc ) {+    std::ostringstream ost;+    ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::INVALID_PARAMETER, errorString_ );+    return;+  }++  MIDIPortRef port;+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);+  CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );+  OSStatus result = MIDIInputPortCreate( data->client,+                                         portNameRef,+                                         midiInputCallback, (void *)&inputData_, &port );+  CFRelease( portNameRef );++  if ( result != noErr ) {+    MIDIClientDispose( data->client );+    errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Get the desired input source identifier.+  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;+  }++  // Make the connection.+  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;+  }++  // Save our api-specific port information.+  data->port = port;++  connected_ = true;+}++void MidiInCore :: openVirtualPort( const std::string &portName )+{+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);++  // Create a virtual MIDI input destination.+  MIDIEndpointRef endpoint;+  CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );+  OSStatus result = MIDIDestinationCreate( data->client,+                                           portNameRef,+                                           midiInputCallback, (void *)&inputData_, &endpoint );+  CFRelease( portNameRef );++  if ( result != noErr ) {+    errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Save our api-specific connection information.+  data->endpoint = endpoint;+}++void MidiInCore :: closePort( void )+{+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);++  if ( data->endpoint ) {+    MIDIEndpointDispose( data->endpoint );+    data->endpoint = 0;+  }++  if ( data->port ) {+    MIDIPortDispose( data->port );+    data->port = 0;+  }++  connected_ = false;+}++void MidiInCore :: setClientName ( const std::string& )+{++  errorString_ = "MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiInCore :: setPortName ( const std::string& )+{++  errorString_ = "MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API!";+  error( RtMidiError::WARNING, errorString_ );++}++unsigned int MidiInCore :: getPortCount()+{+  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );+  return MIDIGetNumberOfSources();+}++// This function was submitted by Douglas Casey Tucker and apparently+// derived largely from PortMidi.+CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )+{+  CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );+  CFStringRef str;++  // Begin with the endpoint's name.+  str = NULL;+  MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );+  if ( str != NULL ) {+    CFStringAppend( result, str );+    CFRelease( str );+  }++  MIDIEntityRef entity = 0;+  MIDIEndpointGetEntity( endpoint, &entity );+  if ( entity == 0 )+    // probably virtual+    return result;++  if ( CFStringGetLength( result ) == 0 ) {+    // endpoint name has zero length -- try the entity+    str = NULL;+    MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );+    if ( str != NULL ) {+      CFStringAppend( result, str );+      CFRelease( str );+    }+  }+  // now consider the device's name+  MIDIDeviceRef device = 0;+  MIDIEntityGetDevice( entity, &device );+  if ( device == 0 )+    return result;++  str = NULL;+  MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );+  if ( CFStringGetLength( result ) == 0 ) {+      CFRelease( result );+      return str;+  }+  if ( str != NULL ) {+    // if an external device has only one entity, throw away+    // the endpoint name and just use the device name+    if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {+      CFRelease( result );+      return str;+    } else {+      if ( CFStringGetLength( str ) == 0 ) {+        CFRelease( str );+        return result;+      }+      // does the entity name already start with the device name?+      // (some drivers do this though they shouldn't)+      // if so, do not prepend+      if ( CFStringCompareWithOptions( result, /* endpoint name */+                                       str /* device name */,+                                       CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {+        // prepend the device name to the entity name+        if ( CFStringGetLength( result ) > 0 )+          CFStringInsert( result, 0, CFSTR(" ") );++        CFStringInsert( result, 0, str );+      }+      CFRelease( str );+    }+  }+  return result;+}++// This function was submitted by Douglas Casey Tucker and apparently+// derived largely from PortMidi.+static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )+{+  CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );+  CFStringRef str;+  OSStatus err;+  int i;++  // Does the endpoint have connections?+  CFDataRef connections = NULL;+  int nConnected = 0;+  bool anyStrings = false;+  err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );+  if ( connections != NULL ) {+    // It has connections, follow them+    // Concatenate the names of all connected devices+    nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);+    if ( nConnected ) {+      const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));+      for ( i=0; i<nConnected; ++i, ++pid ) {+        MIDIUniqueID id = EndianS32_BtoN( *pid );+        MIDIObjectRef connObject;+        MIDIObjectType connObjectType;+        err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );+        if ( err == noErr ) {+          if ( connObjectType == kMIDIObjectType_ExternalSource  ||+               connObjectType == kMIDIObjectType_ExternalDestination ) {+            // Connected to an external device's endpoint (10.3 and later).+            str = EndpointName( (MIDIEndpointRef)(connObject), true );+          } else {+            // Connected to an external device (10.2) (or something else, catch-+            str = NULL;+            MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );+          }+          if ( str != NULL ) {+            if ( anyStrings )+              CFStringAppend( result, CFSTR(", ") );+            else+              anyStrings = true;+            CFStringAppend( result, str );+            CFRelease( str );+          }+        }+      }+    }+    CFRelease( connections );+  }+  if ( anyStrings )+    return result;++  CFRelease( result );++  // Here, either the endpoint had no connections, or we failed to obtain names+  return EndpointName( endpoint, false );+}++std::string MidiInCore :: getPortName( unsigned int portNumber )+{+  CFStringRef nameRef;+  MIDIEndpointRef portRef;+  char name[128];++  std::string stringName;+  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );+  if ( portNumber >= MIDIGetNumberOfSources() ) {+    std::ostringstream ost;+    ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::WARNING, errorString_ );+    return stringName;+  }++  portRef = MIDIGetSource( portNumber );+  nameRef = ConnectedEndpointName( portRef );+  CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 );+  CFRelease( nameRef );++  return stringName = name;+}++//*********************************************************************//+//  API: OS-X+//  Class Definitions: MidiOutCore+//*********************************************************************//++MidiOutCore :: MidiOutCore( const std::string &clientName )+  : MidiOutApi()+{+  MidiOutCore::initialize( clientName );+}++MidiOutCore :: ~MidiOutCore( void )+{+  // Close a connection if it exists.+  MidiOutCore::closePort();++  // Cleanup.+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);+  MIDIClientDispose( data->client );+  if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );+  delete data;+}++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;+  }++  // 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()+{+  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );+  return MIDIGetNumberOfDestinations();+}++std::string MidiOutCore :: getPortName( unsigned int portNumber )+{+  CFStringRef nameRef;+  MIDIEndpointRef portRef;+  char name[128];++  std::string stringName;+  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );+  if ( portNumber >= MIDIGetNumberOfDestinations() ) {+    std::ostringstream ost;+    ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::WARNING, errorString_ );+    return stringName;+  }++  portRef = MIDIGetDestination( portNumber );+  nameRef = ConnectedEndpointName(portRef);+  CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 );+  CFRelease( nameRef );++  return stringName = name;+}++void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName )+{+  if ( connected_ ) {+    errorString_ = "MidiOutCore::openPort: a valid connection already exists!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );+  unsigned int nDest = MIDIGetNumberOfDestinations();+  if (nDest < 1) {+    errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!";+    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );+    return;+  }++  if ( portNumber >= nDest ) {+    std::ostringstream ost;+    ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::INVALID_PARAMETER, errorString_ );+    return;+  }++  MIDIPortRef port;+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);+  CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );+  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;+  }++  // Get the desired output port identifier.+  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;+  }++  // Save our api-specific connection information.+  data->port = port;+  data->destinationId = destination;+  connected_ = true;+}++void MidiOutCore :: closePort( void )+{+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);++  if ( data->endpoint ) {+    MIDIEndpointDispose( data->endpoint );+    data->endpoint = 0;+  }++  if ( data->port ) {+    MIDIPortDispose( data->port );+    data->port = 0;+  }++  connected_ = false;+}++void MidiOutCore :: setClientName ( const std::string& )+{++  errorString_ = "MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiOutCore :: setPortName ( const std::string& )+{++  errorString_ = "MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiOutCore :: openVirtualPort( const std::string &portName )+{+  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);++  if ( data->endpoint ) {+    errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  // Create a virtual MIDI output source.+  MIDIEndpointRef endpoint;+  CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );+  OSStatus result = MIDISourceCreate( data->client, portNameRef, &endpoint );+  CFRelease( portNameRef );++  if ( result != noErr ) {+    errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Save our api-specific connection information.+  data->endpoint = endpoint;+}++void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )+{+  // We use the MIDISendSysex() function to asynchronously send sysex+  // messages.  Otherwise, we use a single CoreMidi MIDIPacket.+  unsigned int nBytes = static_cast<unsigned int> (size);+  if ( nBytes == 0 ) {+    errorString_ = "MidiOutCore::sendMessage: no data in message argument!";+    error( RtMidiError::WARNING, errorString_ );+    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 ))];+  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+    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;+  }++  // 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_ );+    }+  }+}++#endif  // __MACOSX_CORE__+++//*********************************************************************//+//  API: LINUX ALSA SEQUENCER+//*********************************************************************//++// API information found at:+//   - http://www.alsa-project.org/documentation.php#Library++#if defined(__LINUX_ALSA__)++// The ALSA Sequencer API is based on the use of a callback function for+// MIDI input.+//+// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer+// time stamps and other assorted fixes!!!++// If you don't need timestamping for incoming MIDI events, define the+// preprocessor definition AVOID_TIMESTAMPING to save resources+// associated with the ALSA sequencer queues.++#include <pthread.h>+#include <sys/time.h>++// ALSA header file.+#include <alsa/asoundlib.h>++// A structure to hold variables related to the ALSA API+// implementation.+struct AlsaMidiData {+  snd_seq_t *seq;+  unsigned int portNum;+  int vport;+  snd_seq_port_subscribe_t *subscription;+  snd_midi_event_t *coder;+  unsigned int bufferSize;+  unsigned char *buffer;+  pthread_t thread;+  pthread_t dummy_thread_id;+  snd_seq_real_time_t lastTime;+  int queue_id; // an input queue is needed to get timestamped events+  int trigger_fds[2];+};++#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))++//*********************************************************************//+//  API: LINUX ALSA+//  Class Definitions: MidiInAlsa+//*********************************************************************//++static void *alsaMidiHandler( void *ptr )+{+  MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr);+  AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);++  long nBytes;+  double time;+  bool continueSysex = false;+  bool doDecode = false;+  MidiInApi::MidiMessage message;+  int poll_fd_count;+  struct pollfd *poll_fds;++  snd_seq_event_t *ev;+  int result;+  apiData->bufferSize = 32;+  result = snd_midi_event_new( 0, &apiData->coder );+  if ( result < 0 ) {+    data->doInput = false;+    std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n";+    return 0;+  }+  unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );+  if ( buffer == NULL ) {+    data->doInput = false;+    snd_midi_event_free( apiData->coder );+    apiData->coder = 0;+    std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n";+    return 0;+  }+  snd_midi_event_init( apiData->coder );+  snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages++  poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1;+  poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd ));+  snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN );+  poll_fds[0].fd = apiData->trigger_fds[0];+  poll_fds[0].events = POLLIN;++  while ( data->doInput ) {++    if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {+      // No data pending+      if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) {+        if ( poll_fds[0].revents & POLLIN ) {+          bool dummy;+          int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) );+          (void) res;+        }+      }+      continue;+    }++    // If here, there should be data.+    result = snd_seq_event_input( apiData->seq, &ev );+    if ( result == -ENOSPC ) {+      std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n";+      continue;+    }+    else if ( result <= 0 ) {+      std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n";+      perror("System reports");+      continue;+    }++    // This is a bit weird, but we now have to decode an ALSA MIDI+    // event (back) into MIDI bytes.  We'll ignore non-MIDI types.+    if ( !continueSysex ) message.bytes.clear();++    doDecode = false;+    switch ( ev->type ) {++    case SND_SEQ_EVENT_PORT_SUBSCRIBED:+#if defined(__RTMIDI_DEBUG__)+      std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n";+#endif+      break;++    case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:+#if defined(__RTMIDI_DEBUG__)+      std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n";+      std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"+                << (int) ev->data.connect.sender.port+                << ", dest = " << (int) ev->data.connect.dest.client << ":"+                << (int) ev->data.connect.dest.port+                << std::endl;+#endif+      break;++    case SND_SEQ_EVENT_QFRAME: // MIDI time code+      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;+      break;++    case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick+      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;+      break;++    case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick+      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;+      break;++    case SND_SEQ_EVENT_SENSING: // Active sensing+      if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;+      break;++    case SND_SEQ_EVENT_SYSEX:+      if ( (data->ignoreFlags & 0x01) ) break;+      if ( ev->data.ext.len > apiData->bufferSize ) {+        apiData->bufferSize = ev->data.ext.len;+        free( buffer );+        buffer = (unsigned char *) malloc( apiData->bufferSize );+        if ( buffer == NULL ) {+          data->doInput = false;+          std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n";+          break;+        }+      }+      doDecode = true;+      break;++    default:+      doDecode = true;+    }++    if ( doDecode ) {++      nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );+      if ( nBytes > 0 ) {+        // The ALSA sequencer has a maximum buffer size for MIDI sysex+        // events of 256 bytes.  If a device sends sysex messages larger+        // than this, they are segmented into 256 byte chunks.  So,+        // we'll watch for this and concatenate sysex chunks into a+        // single sysex message if necessary.+        if ( !continueSysex )+          message.bytes.assign( buffer, &buffer[nBytes] );+        else+          message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );++        continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );+        if ( !continueSysex ) {++          // Calculate the time stamp:+          message.timeStamp = 0.0;++          // Method 1: Use the system time.+          //(void)gettimeofday(&tv, (struct timezone *)NULL);+          //time = (tv.tv_sec * 1000000) + tv.tv_usec;++          // Method 2: Use the ALSA sequencer event time data.+          // (thanks to Pedro Lopez-Cabanillas!).++          // Using method from:+          // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html++          // Perform the carry for the later subtraction by updating y.+          // Temp var y is timespec because computation requires signed types,+          // while snd_seq_real_time_t has unsigned types.+          snd_seq_real_time_t &x( ev->time.time );+          struct timespec y;+          y.tv_nsec = apiData->lastTime.tv_nsec;+          y.tv_sec = apiData->lastTime.tv_sec;+          if ( x.tv_nsec < y.tv_nsec ) {+              int nsec = (y.tv_nsec - (int)x.tv_nsec) / 1000000000 + 1;+              y.tv_nsec -= 1000000000 * nsec;+              y.tv_sec += nsec;+          }+          if ( x.tv_nsec - y.tv_nsec > 1000000000 ) {+              int nsec = ((int)x.tv_nsec - y.tv_nsec) / 1000000000;+              y.tv_nsec += 1000000000 * nsec;+              y.tv_sec -= nsec;+          }++          // Compute the time difference.+          time = (int)x.tv_sec - y.tv_sec + ((int)x.tv_nsec - y.tv_nsec)*1e-9;++          apiData->lastTime = ev->time.time;++          if ( data->firstMessage == true )+            data->firstMessage = false;+          else+            message.timeStamp = time;+        }+        else {+#if defined(__RTMIDI_DEBUG__)+          std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";+#endif+        }+      }+    }++    snd_seq_free_event( ev );+    if ( message.bytes.size() == 0 || continueSysex ) continue;++    if ( data->usingCallback ) {+      RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;+      callback( message.timeStamp, &message.bytes, data->userData );+    }+    else {+      // As long as we haven't reached our queue size limit, push the message.+      if ( !data->queue.push( message ) )+        std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n";+    }+  }++  if ( buffer ) free( buffer );+  snd_midi_event_free( apiData->coder );+  apiData->coder = 0;+  apiData->thread = apiData->dummy_thread_id;+  return 0;+}++MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit )+  : MidiInApi( queueSizeLimit )+{+  MidiInAlsa::initialize( clientName );+}++MidiInAlsa :: ~MidiInAlsa()+{+  // Close a connection if it exists.+  MidiInAlsa::closePort();++  // Shutdown the input thread.+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( inputData_.doInput ) {+    inputData_.doInput = false;+    int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) );+    (void) res;+    if ( !pthread_equal(data->thread, data->dummy_thread_id) )+      pthread_join( data->thread, NULL );+  }++  // Cleanup.+  close ( data->trigger_fds[0] );+  close ( data->trigger_fds[1] );+  if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );+#ifndef AVOID_TIMESTAMPING+  snd_seq_free_queue( data->seq, data->queue_id );+#endif+  snd_seq_close( data->seq );+  delete data;+}++void MidiInAlsa :: initialize( const std::string& clientName )+{+  // Set up the ALSA sequencer client.+  snd_seq_t *seq;+  int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK );+  if ( result < 0 ) {+    errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Set client name.+  snd_seq_set_client_name( seq, clientName.c_str() );++  // Save our api-specific connection information.+  AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;+  data->seq = seq;+  data->portNum = -1;+  data->vport = -1;+  data->subscription = 0;+  data->dummy_thread_id = pthread_self();+  data->thread = data->dummy_thread_id;+  data->trigger_fds[0] = -1;+  data->trigger_fds[1] = -1;+  apiData_ = (void *) data;+  inputData_.apiData = (void *) data;++  if ( pipe(data->trigger_fds) == -1 ) {+    errorString_ = "MidiInAlsa::initialize: error creating pipe objects.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Create the input queue+#ifndef AVOID_TIMESTAMPING+  data->queue_id = snd_seq_alloc_named_queue( seq, "RtMidi Queue" );+  // Set arbitrary tempo (mm=100) and resolution (240)+  snd_seq_queue_tempo_t *qtempo;+  snd_seq_queue_tempo_alloca( &qtempo );+  snd_seq_queue_tempo_set_tempo( qtempo, 600000 );+  snd_seq_queue_tempo_set_ppq( qtempo, 240 );+  snd_seq_set_queue_tempo( data->seq, data->queue_id, qtempo );+  snd_seq_drain_output( data->seq );+#endif+}++// This function is used to count or get the pinfo structure for a given port number.+unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )+{+  snd_seq_client_info_t *cinfo;+  int client;+  int count = 0;+  snd_seq_client_info_alloca( &cinfo );++  snd_seq_client_info_set_client( cinfo, -1 );+  while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {+    client = snd_seq_client_info_get_client( cinfo );+    if ( client == 0 ) continue;+    // Reset query info+    snd_seq_port_info_set_client( pinfo, client );+    snd_seq_port_info_set_port( pinfo, -1 );+    while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {+      unsigned int atyp = snd_seq_port_info_get_type( pinfo );+      if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&+           ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) &&+           ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue;++      unsigned int caps = snd_seq_port_info_get_capability( pinfo );+      if ( ( caps & type ) != type ) continue;+      if ( count == portNumber ) return 1;+      ++count;+    }+  }++  // If a negative portNumber was used, return the port count.+  if ( portNumber < 0 ) return count;+  return 0;+}++unsigned int MidiInAlsa :: getPortCount()+{+  snd_seq_port_info_t *pinfo;+  snd_seq_port_info_alloca( &pinfo );++  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );+}++std::string MidiInAlsa :: getPortName( unsigned int portNumber )+{+  snd_seq_client_info_t *cinfo;+  snd_seq_port_info_t *pinfo;+  snd_seq_client_info_alloca( &cinfo );+  snd_seq_port_info_alloca( &pinfo );++  std::string stringName;+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {+    int cnum = snd_seq_port_info_get_client( pinfo );+    snd_seq_get_any_client_info( data->seq, cnum, cinfo );+    std::ostringstream os;+    os << snd_seq_client_info_get_name( cinfo );+    os << ":";+    os << snd_seq_port_info_get_name( pinfo );+    os << " ";                                    // These lines added to make sure devices are listed+    os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names+    os << ":";+    os << snd_seq_port_info_get_port( pinfo );+    stringName = os.str();+    return stringName;+  }++  // If we get here, we didn't find a match.+  errorString_ = "MidiInAlsa::getPortName: error looking for port name!";+  error( RtMidiError::WARNING, errorString_ );+  return stringName;+}++void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName )+{+  if ( connected_ ) {+    errorString_ = "MidiInAlsa::openPort: a valid connection already exists!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  unsigned int nSrc = this->getPortCount();+  if ( nSrc < 1 ) {+    errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!";+    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );+    return;+  }++  snd_seq_port_info_t *src_pinfo;+  snd_seq_port_info_alloca( &src_pinfo );+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {+    std::ostringstream ost;+    ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::INVALID_PARAMETER, errorString_ );+    return;+  }++  snd_seq_addr_t sender, receiver;+  sender.client = snd_seq_port_info_get_client( src_pinfo );+  sender.port = snd_seq_port_info_get_port( src_pinfo );+  receiver.client = snd_seq_client_id( data->seq );++  snd_seq_port_info_t *pinfo;+  snd_seq_port_info_alloca( &pinfo );+  if ( data->vport < 0 ) {+    snd_seq_port_info_set_client( pinfo, 0 );+    snd_seq_port_info_set_port( pinfo, 0 );+    snd_seq_port_info_set_capability( pinfo,+                                      SND_SEQ_PORT_CAP_WRITE |+                                      SND_SEQ_PORT_CAP_SUBS_WRITE );+    snd_seq_port_info_set_type( pinfo,+                                SND_SEQ_PORT_TYPE_MIDI_GENERIC |+                                SND_SEQ_PORT_TYPE_APPLICATION );+    snd_seq_port_info_set_midi_channels(pinfo, 16);+#ifndef AVOID_TIMESTAMPING+    snd_seq_port_info_set_timestamping( pinfo, 1 );+    snd_seq_port_info_set_timestamp_real( pinfo, 1 );+    snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id );+#endif+    snd_seq_port_info_set_name( pinfo,  portName.c_str() );+    data->vport = snd_seq_create_port( data->seq, pinfo );++    if ( data->vport < 0 ) {+      errorString_ = "MidiInAlsa::openPort: ALSA error creating input port.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }+    data->vport = snd_seq_port_info_get_port( pinfo );+  }++  receiver.port = data->vport;++  if ( !data->subscription ) {+    // Make subscription+    if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) {+      errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }+    snd_seq_port_subscribe_set_sender( data->subscription, &sender );+    snd_seq_port_subscribe_set_dest( data->subscription, &receiver );+    if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) {+      snd_seq_port_subscribe_free( data->subscription );+      data->subscription = 0;+      errorString_ = "MidiInAlsa::openPort: ALSA error making port connection.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }+  }++  if ( inputData_.doInput == false ) {+    // Start the input queue+#ifndef AVOID_TIMESTAMPING+    snd_seq_start_queue( data->seq, data->queue_id, NULL );+    snd_seq_drain_output( data->seq );+#endif+    // Start our MIDI input thread.+    pthread_attr_t attr;+    pthread_attr_init( &attr );+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );++    inputData_.doInput = true;+    int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ );+    pthread_attr_destroy( &attr );+    if ( err ) {+      snd_seq_unsubscribe_port( data->seq, data->subscription );+      snd_seq_port_subscribe_free( data->subscription );+      data->subscription = 0;+      inputData_.doInput = false;+      errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";+      error( RtMidiError::THREAD_ERROR, errorString_ );+      return;+    }+  }++  connected_ = true;+}++void MidiInAlsa :: openVirtualPort( const std::string &portName )+{+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( data->vport < 0 ) {+    snd_seq_port_info_t *pinfo;+    snd_seq_port_info_alloca( &pinfo );+    snd_seq_port_info_set_capability( pinfo,+                                      SND_SEQ_PORT_CAP_WRITE |+                                      SND_SEQ_PORT_CAP_SUBS_WRITE );+    snd_seq_port_info_set_type( pinfo,+                                SND_SEQ_PORT_TYPE_MIDI_GENERIC |+                                SND_SEQ_PORT_TYPE_APPLICATION );+    snd_seq_port_info_set_midi_channels( pinfo, 16 );+#ifndef AVOID_TIMESTAMPING+    snd_seq_port_info_set_timestamping( pinfo, 1 );+    snd_seq_port_info_set_timestamp_real( pinfo, 1 );+    snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id );+#endif+    snd_seq_port_info_set_name( pinfo, portName.c_str() );+    data->vport = snd_seq_create_port( data->seq, pinfo );++    if ( data->vport < 0 ) {+      errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }+    data->vport = snd_seq_port_info_get_port( pinfo );+  }++  if ( inputData_.doInput == false ) {+    // Wait for old thread to stop, if still running+    if ( !pthread_equal( data->thread, data->dummy_thread_id ) )+      pthread_join( data->thread, NULL );++    // Start the input queue+#ifndef AVOID_TIMESTAMPING+    snd_seq_start_queue( data->seq, data->queue_id, NULL );+    snd_seq_drain_output( data->seq );+#endif+    // Start our MIDI input thread.+    pthread_attr_t attr;+    pthread_attr_init( &attr );+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );++    inputData_.doInput = true;+    int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ );+    pthread_attr_destroy( &attr );+    if ( err ) {+      if ( data->subscription ) {+        snd_seq_unsubscribe_port( data->seq, data->subscription );+        snd_seq_port_subscribe_free( data->subscription );+        data->subscription = 0;+      }+      inputData_.doInput = false;+      errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";+      error( RtMidiError::THREAD_ERROR, errorString_ );+      return;+    }+  }+}++void MidiInAlsa :: closePort( void )+{+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);++  if ( connected_ ) {+    if ( data->subscription ) {+      snd_seq_unsubscribe_port( data->seq, data->subscription );+      snd_seq_port_subscribe_free( data->subscription );+      data->subscription = 0;+    }+    // Stop the input queue+#ifndef AVOID_TIMESTAMPING+    snd_seq_stop_queue( data->seq, data->queue_id, NULL );+    snd_seq_drain_output( data->seq );+#endif+    connected_ = false;+  }++  // Stop thread to avoid triggering the callback, while the port is intended to be closed+  if ( inputData_.doInput ) {+    inputData_.doInput = false;+    int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) );+    (void) res;+    if ( !pthread_equal( data->thread, data->dummy_thread_id ) )+      pthread_join( data->thread, NULL );+  }+}++void MidiInAlsa :: setClientName( const std::string &clientName )+{++  AlsaMidiData *data = static_cast<AlsaMidiData *> ( apiData_ );+  snd_seq_set_client_name( data->seq, clientName.c_str() );++}++void MidiInAlsa :: setPortName( const std::string &portName )+{+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  snd_seq_port_info_t *pinfo;+  snd_seq_port_info_alloca( &pinfo );+  snd_seq_get_port_info( data->seq, data->vport, pinfo );+  snd_seq_port_info_set_name( pinfo, portName.c_str() );+  snd_seq_set_port_info( data->seq, data->vport, pinfo );+}++//*********************************************************************//+//  API: LINUX ALSA+//  Class Definitions: MidiOutAlsa+//*********************************************************************//++MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi()+{+  MidiOutAlsa::initialize( clientName );+}++MidiOutAlsa :: ~MidiOutAlsa()+{+  // Close a connection if it exists.+  MidiOutAlsa::closePort();++  // Cleanup.+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );+  if ( data->coder ) snd_midi_event_free( data->coder );+  if ( data->buffer ) free( data->buffer );+  snd_seq_close( data->seq );+  delete data;+}++void MidiOutAlsa :: initialize( const std::string& clientName )+{+  // Set up the ALSA sequencer client.+  snd_seq_t *seq;+  int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );+  if ( result1 < 0 ) {+    errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Set client name.+  snd_seq_set_client_name( seq, clientName.c_str() );++  // Save our api-specific connection information.+  AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;+  data->seq = seq;+  data->portNum = -1;+  data->vport = -1;+  data->bufferSize = 32;+  data->coder = 0;+  data->buffer = 0;+  int result = snd_midi_event_new( data->bufferSize, &data->coder );+  if ( result < 0 ) {+    delete data;+    errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }+  data->buffer = (unsigned char *) malloc( data->bufferSize );+  if ( data->buffer == NULL ) {+    delete data;+    errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";+    error( RtMidiError::MEMORY_ERROR, errorString_ );+    return;+  }+  snd_midi_event_init( data->coder );+  apiData_ = (void *) data;+}++unsigned int MidiOutAlsa :: getPortCount()+{+  snd_seq_port_info_t *pinfo;+  snd_seq_port_info_alloca( &pinfo );++  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );+}++std::string MidiOutAlsa :: getPortName( unsigned int portNumber )+{+  snd_seq_client_info_t *cinfo;+  snd_seq_port_info_t *pinfo;+  snd_seq_client_info_alloca( &cinfo );+  snd_seq_port_info_alloca( &pinfo );++  std::string stringName;+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {+    int cnum = snd_seq_port_info_get_client( pinfo );+    snd_seq_get_any_client_info( data->seq, cnum, cinfo );+    std::ostringstream os;+    os << snd_seq_client_info_get_name( cinfo );+    os << ":";+    os << snd_seq_port_info_get_name( pinfo );+    os << " ";                                    // These lines added to make sure devices are listed+    os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names+    os << ":";+    os << snd_seq_port_info_get_port( pinfo );+    stringName = os.str();+    return stringName;+  }++  // If we get here, we didn't find a match.+  errorString_ = "MidiOutAlsa::getPortName: error looking for port name!";+  error( RtMidiError::WARNING, errorString_ );+  return stringName;+}++void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName )+{+  if ( connected_ ) {+    errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  unsigned int nSrc = this->getPortCount();+  if ( nSrc < 1 ) {+    errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!";+    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );+    return;+  }++  snd_seq_port_info_t *pinfo;+  snd_seq_port_info_alloca( &pinfo );+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {+    std::ostringstream ost;+    ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::INVALID_PARAMETER, errorString_ );+    return;+  }++  snd_seq_addr_t sender, receiver;+  receiver.client = snd_seq_port_info_get_client( pinfo );+  receiver.port = snd_seq_port_info_get_port( pinfo );+  sender.client = snd_seq_client_id( data->seq );++  if ( data->vport < 0 ) {+    data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),+                                              SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,+                                              SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );+    if ( data->vport < 0 ) {+      errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }+  }++  sender.port = data->vport;++  // Make subscription+  if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) {+    snd_seq_port_subscribe_free( data->subscription );+    errorString_ = "MidiOutAlsa::openPort: error allocating port subscription.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }+  snd_seq_port_subscribe_set_sender( data->subscription, &sender );+  snd_seq_port_subscribe_set_dest( data->subscription, &receiver );+  snd_seq_port_subscribe_set_time_update( data->subscription, 1 );+  snd_seq_port_subscribe_set_time_real( data->subscription, 1 );+  if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) {+    snd_seq_port_subscribe_free( data->subscription );+    errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  connected_ = true;+}++void MidiOutAlsa :: closePort( void )+{+  if ( connected_ ) {+    AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+    snd_seq_unsubscribe_port( data->seq, data->subscription );+    snd_seq_port_subscribe_free( data->subscription );+    data->subscription = 0;+    connected_ = false;+  }+}++void MidiOutAlsa :: setClientName( const std::string &clientName )+{++    AlsaMidiData *data = static_cast<AlsaMidiData *> ( apiData_ );+    snd_seq_set_client_name( data->seq, clientName.c_str() );++}++void MidiOutAlsa :: setPortName( const std::string &portName )+{+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  snd_seq_port_info_t *pinfo;+  snd_seq_port_info_alloca( &pinfo );+  snd_seq_get_port_info( data->seq, data->vport, pinfo );+  snd_seq_port_info_set_name( pinfo, portName.c_str() );+  snd_seq_set_port_info( data->seq, data->vport, pinfo );+}++void MidiOutAlsa :: openVirtualPort( const std::string &portName )+{+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  if ( data->vport < 0 ) {+    data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),+                                              SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,+                                              SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );++    if ( data->vport < 0 ) {+      errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+    }+  }+}++void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )+{+  int result;+  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);+  unsigned int nBytes = static_cast<unsigned int> (size);+  if ( nBytes > data->bufferSize ) {+    data->bufferSize = nBytes;+    result = snd_midi_event_resize_buffer( data->coder, nBytes );+    if ( result != 0 ) {+      errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }+    free (data->buffer);+    data->buffer = (unsigned char *) malloc( data->bufferSize );+    if ( data->buffer == NULL ) {+      errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";+      error( RtMidiError::MEMORY_ERROR, errorString_ );+      return;+    }+  }++  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;+  }+  snd_seq_drain_output( data->seq );+}++#endif // __LINUX_ALSA__+++//*********************************************************************//+//  API: Windows Multimedia Library (MM)+//*********************************************************************//++// API information deciphered from:+//  - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp++// Thanks to Jean-Baptiste Berruchon for the sysex code.++#if defined(__WINDOWS_MM__)++// The Windows MM API is based on the use of a callback function for+// MIDI input.  We convert the system specific time stamps to delta+// time values.++// Windows MM MIDI header files.+#include <windows.h>+#include <mmsystem.h>++// Convert a null-terminated wide string or ANSI-encoded string to UTF-8.+static std::string ConvertToUTF8(const TCHAR *str)+{+  std::string u8str;+  const WCHAR *wstr = L"";+#if defined( UNICODE ) || defined( _UNICODE )+  wstr = str;+#else+  // Convert from ANSI encoding to wide string+  int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );+  std::wstring wstrtemp;+  if ( wlength )+  {+    wstrtemp.assign( wlength - 1, 0 );+    MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength );+    wstr = &wstrtemp[0];+  }+#endif+  // Convert from wide string to UTF-8+  int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL );+  if ( length )+  {+    u8str.assign( length - 1, 0 );+    length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL );+  }+  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 {+  HMIDIIN inHandle;    // Handle to Midi Input Device+  HMIDIOUT outHandle;  // Handle to Midi Output Device+  DWORD lastTime;+  MidiInApi::MidiMessage message;+  LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];+  CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo+};++//*********************************************************************//+//  API: Windows MM+//  Class Definitions: MidiInWinMM+//*********************************************************************//++static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/,+                                        UINT inputStatus,+                                        DWORD_PTR instancePtr,+                                        DWORD_PTR midiMessage,+                                        DWORD timestamp )+{+  if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;++  //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr);+  MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr;+  WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);++  // Calculate time stamp.+  if ( data->firstMessage == true ) {+    apiData->message.timeStamp = 0.0;+    data->firstMessage = false;+  }+  else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;++  if ( inputStatus == MIM_DATA ) { // Channel or system message++    // Make sure the first byte is a status byte.+    unsigned char status = (unsigned char) (midiMessage & 0x000000FF);+    if ( !(status & 0x80) ) return;++    // Determine the number of bytes in the MIDI message.+    unsigned short nBytes = 1;+    if ( status < 0xC0 ) nBytes = 3;+    else if ( status < 0xE0 ) nBytes = 2;+    else if ( status < 0xF0 ) nBytes = 3;+    else if ( status == 0xF1 ) {+      if ( data->ignoreFlags & 0x02 ) return;+      else nBytes = 2;+    }+    else if ( status == 0xF2 ) nBytes = 3;+    else if ( status == 0xF3 ) nBytes = 2;+    else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {+      // A MIDI timing tick message and we're ignoring it.+      return;+    }+    else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {+      // A MIDI active sensing message and we're ignoring it.+      return;+    }++    // Copy bytes to our MIDI message.+    unsigned char *ptr = (unsigned char *) &midiMessage;+    for ( int i=0; i<nBytes; ++i ) apiData->message.bytes.push_back( *ptr++ );+  }+  else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )+    MIDIHDR *sysex = ( MIDIHDR *) midiMessage;+    if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) {+      // Sysex message and we're not ignoring it+      for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i )+        apiData->message.bytes.push_back( sysex->lpData[i] );+    }++    // The WinMM API requires that the sysex buffer be requeued after+    // input of each sysex message.  Even if we are ignoring sysex+    // messages, we still need to requeue the buffer in case the user+    // decides to not ignore sysex messages in the future.  However,+    // it seems that WinMM calls this function with an empty sysex+    // buffer when an application closes and in this case, we should+    // avoid requeueing it, else the computer suddenly reboots after+    // one or two minutes.+    if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {+      //if ( sysex->dwBytesRecorded > 0 ) {+      EnterCriticalSection( &(apiData->_mutex) );+      MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );+      LeaveCriticalSection( &(apiData->_mutex) );+      if ( result != MMSYSERR_NOERROR )+        std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";++      if ( data->ignoreFlags & 0x01 ) return;+    }+    else return;+  }++  // Save the time of the last non-filtered message+  apiData->lastTime = timestamp;++  if ( data->usingCallback ) {+    RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;+    callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );+  }+  else {+    // As long as we haven't reached our queue size limit, push the message.+    if ( !data->queue.push( apiData->message ) )+      std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n";+  }++  // Clear the vector for the next input message.+  apiData->message.bytes.clear();+}++MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit )+  : MidiInApi( queueSizeLimit )+{+  MidiInWinMM::initialize( clientName );+}++MidiInWinMM :: ~MidiInWinMM()+{+  // Close a connection if it exists.+  MidiInWinMM::closePort();++  WinMidiData *data = static_cast<WinMidiData *> (apiData_);+  DeleteCriticalSection( &(data->_mutex) );++  // Cleanup.+  delete data;+}++void MidiInWinMM :: initialize( const std::string& /*clientName*/ )+{+  // We'll issue a warning here if no devices are available but not+  // throw an error since the user can plugin something later.+  unsigned int nDevices = midiInGetNumDevs();+  if ( nDevices == 0 ) {+    errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available.";+    error( RtMidiError::WARNING, errorString_ );+  }++  // Save our api-specific connection information.+  WinMidiData *data = (WinMidiData *) new WinMidiData;+  apiData_ = (void *) data;+  inputData_.apiData = (void *) data;+  data->message.bytes.clear();  // needs to be empty for first input message++  if ( !InitializeCriticalSectionAndSpinCount( &(data->_mutex), 0x00000400 ) ) {+    errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed.";+    error( RtMidiError::WARNING, errorString_ );+  }+}++void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ )+{+  if ( connected_ ) {+    errorString_ = "MidiInWinMM::openPort: a valid connection already exists!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  unsigned int nDevices = midiInGetNumDevs();+  if (nDevices == 0) {+    errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!";+    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );+    return;+  }++  if ( portNumber >= nDevices ) {+    std::ostringstream ost;+    ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::INVALID_PARAMETER, errorString_ );+    return;+  }++  WinMidiData *data = static_cast<WinMidiData *> (apiData_);+  MMRESULT result = midiInOpen( &data->inHandle,+                                portNumber,+                                (DWORD_PTR)&midiInputCallback,+                                (DWORD_PTR)&inputData_,+                                CALLBACK_FUNCTION );+  if ( result != MMSYSERR_NOERROR ) {+    errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Allocate and init the sysex buffers.+  for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++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]->dwUser = i; // We use the dwUser parameter as buffer indicator+    data->sysexBuffer[i]->dwFlags = 0;++    result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );+    if ( result != MMSYSERR_NOERROR ) {+      midiInClose( data->inHandle );+      data->inHandle = 0;+      errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader).";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }++    // Register the buffer.+    result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );+    if ( result != MMSYSERR_NOERROR ) {+      midiInClose( data->inHandle );+      data->inHandle = 0;+      errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer).";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }+  }++  result = midiInStart( data->inHandle );+  if ( result != MMSYSERR_NOERROR ) {+    midiInClose( data->inHandle );+    data->inHandle = 0;+    errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  connected_ = true;+}++void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ )+{+  // This function cannot be implemented for the Windows MM MIDI API.+  errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";+  error( RtMidiError::WARNING, errorString_ );+}++void MidiInWinMM :: closePort( void )+{+  if ( connected_ ) {+    WinMidiData *data = static_cast<WinMidiData *> (apiData_);+    EnterCriticalSection( &(data->_mutex) );+    midiInReset( data->inHandle );+    midiInStop( data->inHandle );++    for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {+      int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));+      delete [] data->sysexBuffer[i]->lpData;+      delete [] data->sysexBuffer[i];+      if ( result != MMSYSERR_NOERROR ) {+        midiInClose( data->inHandle );+        data->inHandle = 0;+        errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";+        error( RtMidiError::DRIVER_ERROR, errorString_ );+        return;+      }+    }++    midiInClose( data->inHandle );+    data->inHandle = 0;+    connected_ = false;+    LeaveCriticalSection( &(data->_mutex) );+  }+}++void MidiInWinMM :: setClientName ( const std::string& )+{++  errorString_ = "MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiInWinMM :: setPortName ( const std::string& )+{++  errorString_ = "MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!";+  error( RtMidiError::WARNING, errorString_ );++}++unsigned int MidiInWinMM :: getPortCount()+{+  return midiInGetNumDevs();+}++std::string MidiInWinMM :: getPortName( unsigned int portNumber )+{+  std::string stringName;+  unsigned int nDevices = midiInGetNumDevs();+  if ( portNumber >= nDevices ) {+    std::ostringstream ost;+    ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::WARNING, errorString_ );+    return stringName;+  }++  MIDIINCAPS deviceCaps;+  midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));+  stringName = ConvertToUTF8( deviceCaps.szPname );++  // Next lines added to add the portNumber to the name so that+  // the device's names are sure to be listed with individual names+  // even when they have the same brand name+#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES+  std::ostringstream os;+  os << " ";+  os << portNumber;+  stringName += os.str();+#endif++  return stringName;+}++//*********************************************************************//+//  API: Windows MM+//  Class Definitions: MidiOutWinMM+//*********************************************************************//++MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi()+{+  MidiOutWinMM::initialize( clientName );+}++MidiOutWinMM :: ~MidiOutWinMM()+{+  // Close a connection if it exists.+  MidiOutWinMM::closePort();++  // Cleanup.+  WinMidiData *data = static_cast<WinMidiData *> (apiData_);+  delete data;+}++void MidiOutWinMM :: initialize( const std::string& /*clientName*/ )+{+  // We'll issue a warning here if no devices are available but not+  // throw an error since the user can plug something in later.+  unsigned int nDevices = midiOutGetNumDevs();+  if ( nDevices == 0 ) {+    errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available.";+    error( RtMidiError::WARNING, errorString_ );+  }++  // Save our api-specific connection information.+  WinMidiData *data = (WinMidiData *) new WinMidiData;+  apiData_ = (void *) data;+}++unsigned int MidiOutWinMM :: getPortCount()+{+  return midiOutGetNumDevs();+}++std::string MidiOutWinMM :: getPortName( unsigned int portNumber )+{+  std::string stringName;+  unsigned int nDevices = midiOutGetNumDevs();+  if ( portNumber >= nDevices ) {+    std::ostringstream ost;+    ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::WARNING, errorString_ );+    return stringName;+  }++  MIDIOUTCAPS deviceCaps;+  midiOutGetDevCaps( portNumber, &deviceCaps, sizeof( MIDIOUTCAPS ) );+  stringName = ConvertToUTF8( deviceCaps.szPname );++  // Next lines added to add the portNumber to the name so that+  // the device's names are sure to be listed with individual names+  // even when they have the same brand name+  std::ostringstream os;+#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES+  os << " ";+  os << portNumber;+  stringName += os.str();+#endif++  return stringName;+}++void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ )+{+  if ( connected_ ) {+    errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  unsigned int nDevices = midiOutGetNumDevs();+  if ( nDevices < 1 ) {+    errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!";+    error( RtMidiError::NO_DEVICES_FOUND, errorString_ );+    return;+  }++  if ( portNumber >= nDevices ) {+    std::ostringstream ost;+    ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::INVALID_PARAMETER, errorString_ );+    return;+  }++  WinMidiData *data = static_cast<WinMidiData *> (apiData_);+  MMRESULT result = midiOutOpen( &data->outHandle,+                                 portNumber,+                                 (DWORD)NULL,+                                 (DWORD)NULL,+                                 CALLBACK_NULL );+  if ( result != MMSYSERR_NOERROR ) {+    errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port.";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  connected_ = true;+}++void MidiOutWinMM :: closePort( void )+{+  if ( connected_ ) {+    WinMidiData *data = static_cast<WinMidiData *> (apiData_);+    midiOutReset( data->outHandle );+    midiOutClose( data->outHandle );+    data->outHandle = 0;+    connected_ = false;+  }+}++void MidiOutWinMM :: setClientName ( const std::string& )+{++  errorString_ = "MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiOutWinMM :: setPortName ( const std::string& )+{++  errorString_ = "MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ )+{+  // This function cannot be implemented for the Windows MM MIDI API.+  errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";+  error( RtMidiError::WARNING, errorString_ );+}++void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size )+{+  if ( !connected_ ) return;++  unsigned int nBytes = static_cast<unsigned int>(size);+  if ( nBytes == 0 ) {+    errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  MMRESULT result;+  WinMidiData *data = static_cast<WinMidiData *> (apiData_);+  if ( message[0] == 0xF0 ) { // Sysex message++    // Allocate buffer for sysex data.+    char *buffer = (char *) malloc( nBytes );+    if ( buffer == NULL ) {+      errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!";+      error( RtMidiError::MEMORY_ERROR, errorString_ );+      return;+    }++    // Copy data to buffer.+    for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message[i];++    // Create and prepare MIDIHDR structure.+    MIDIHDR sysex;+    sysex.lpData = (LPSTR) buffer;+    sysex.dwBufferLength = nBytes;+    sysex.dwFlags = 0;+    result = midiOutPrepareHeader( data->outHandle,  &sysex, sizeof( MIDIHDR ) );+    if ( result != MMSYSERR_NOERROR ) {+      free( buffer );+      errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }++    // Send the message.+    result = midiOutLongMsg( data->outHandle, &sysex, sizeof( MIDIHDR ) );+    if ( result != MMSYSERR_NOERROR ) {+      free( buffer );+      errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+      return;+    }++    // Unprepare the buffer and MIDIHDR.+    while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof ( MIDIHDR ) ) ) Sleep( 1 );+    free( buffer );+  }+  else { // Channel or system message.++    // Make sure the message size isn't too big.+    if ( nBytes > 3 ) {+      errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!";+      error( RtMidiError::WARNING, errorString_ );+      return;+    }++    // Pack MIDI bytes into double word.+    DWORD packet;+    unsigned char *ptr = (unsigned char *) &packet;+    for ( unsigned int i=0; i<nBytes; ++i ) {+      *ptr = message[i];+      ++ptr;+    }++    // Send the message immediately.+    result = midiOutShortMsg( data->outHandle, packet );+    if ( result != MMSYSERR_NOERROR ) {+      errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message.";+      error( RtMidiError::DRIVER_ERROR, errorString_ );+    }+  }+}++#endif  // __WINDOWS_MM__+++//*********************************************************************//+//  API: UNIX JACK+//+//  Written primarily by Alexander Svetalkin, with updates for delta+//  time by Gary Scavone, April 2011.+//+//  *********************************************************************//++#if defined(__UNIX_JACK__)++// JACK header files+#include <jack/jack.h>+#include <jack/midiport.h>+#include <jack/ringbuffer.h>+#ifdef HAVE_SEMAPHORE+  #include <semaphore.h>+#endif++#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer++struct JackMidiData {+  jack_client_t *client;+  jack_port_t *port;+  jack_ringbuffer_t *buffSize;+  jack_ringbuffer_t *buffMessage;+  jack_time_t lastTime;+#ifdef HAVE_SEMAPHORE+  sem_t sem_cleanup;+  sem_t sem_needpost;+#endif+  MidiInApi :: RtMidiInData *rtMidiIn;+  };++//*********************************************************************//+//  API: JACK+//  Class Definitions: MidiInJack+//*********************************************************************//++static int jackProcessIn( jack_nframes_t nframes, void *arg )+{+  JackMidiData *jData = (JackMidiData *) arg;+  MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn;+  jack_midi_event_t event;+  jack_time_t time;++  // Is port created?+  if ( jData->port == NULL ) return 0;++  void *buff = jack_port_get_buffer( jData->port, nframes );+  bool& continueSysex = rtData->continueSysex;+  unsigned char& ignoreFlags = rtData->ignoreFlags;++  // We have midi events in buffer+  int evCount = jack_midi_get_event_count( buff );+  for (int j = 0; j < evCount; j++) {+    MidiInApi::MidiMessage& message = rtData->message;+    jack_midi_event_get( &event, buff, j );++    // Compute the delta time.+    time = jack_get_time();+    if ( rtData->firstMessage == true ) {+      message.timeStamp = 0.0;+      rtData->firstMessage = false;+    } else+      message.timeStamp = ( time - jData->lastTime ) * 0.000001;++    jData->lastTime = time;++    if ( !continueSysex )+      message.bytes.clear();++    if ( !( ( continueSysex || event.buffer[0] == 0xF0 ) && ( ignoreFlags & 0x01 ) ) ) {+      // Unless this is a (possibly continued) SysEx message and we're ignoring SysEx,+      // copy the event buffer into the MIDI message struct.+      for ( unsigned int i = 0; i < event.size; i++ )+        message.bytes.push_back( event.buffer[i] );+    }++    switch ( event.buffer[0] ) {+      case 0xF0:+        // Start of a SysEx message+        continueSysex = event.buffer[event.size - 1] != 0xF7;+        if ( ignoreFlags & 0x01 ) continue;+        break;+      case 0xF1:+      case 0xF8:+        // MIDI Time Code or Timing Clock message+        if ( ignoreFlags & 0x02 ) continue;+        break;+      case 0xFE:+        // Active Sensing message+        if ( ignoreFlags & 0x04 ) continue;+        break;+      default:+        if ( continueSysex ) {+          // Continuation of a SysEx message+          continueSysex = event.buffer[event.size - 1] != 0xF7;+          if ( ignoreFlags & 0x01 ) continue;+        }+        // All other MIDI messages+    }++    if ( !continueSysex ) {+      // If not a continuation of a SysEx message,+      // invoke the user callback function or queue the message.+      if ( rtData->usingCallback ) {+        RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;+        callback( message.timeStamp, &message.bytes, rtData->userData );+      }+      else {+        // As long as we haven't reached our queue size limit, push the message.+        if ( !rtData->queue.push( message ) )+          std::cerr << "\nMidiInJack: message queue limit reached!!\n\n";+      }+    }+  }++  return 0;+}++MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit )+  : MidiInApi( queueSizeLimit )+{+  MidiInJack::initialize( clientName );+}++void MidiInJack :: initialize( const std::string& clientName )+{+  JackMidiData *data = new JackMidiData;+  apiData_ = (void *) data;++  data->rtMidiIn = &inputData_;+  data->port = NULL;+  data->client = NULL;+  this->clientName = clientName;++  connect();+}++void MidiInJack :: connect()+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  if ( data->client )+    return;++  // Initialize JACK client+  if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {+    errorString_ = "MidiInJack::initialize: JACK server not running?";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  jack_set_process_callback( data->client, jackProcessIn, data );+  jack_activate( data->client );+}++MidiInJack :: ~MidiInJack()+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  MidiInJack::closePort();++  if ( data->client )+    jack_client_close( data->client );+  delete data;+}++void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);++  connect();++  // Creating new port+  if ( data->port == NULL )+    data->port = jack_port_register( data->client, portName.c_str(),+                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );++  if ( data->port == NULL ) {+    errorString_ = "MidiInJack::openPort: JACK error creating port";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Connecting to the output+  std::string name = getPortName( portNumber );+  jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );++  connected_ = true;+}++void MidiInJack :: openVirtualPort( const std::string &portName )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);++  connect();+  if ( data->port == NULL )+    data->port = jack_port_register( data->client, portName.c_str(),+                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );++  if ( data->port == NULL ) {+    errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+  }+}++unsigned int MidiInJack :: getPortCount()+{+  int count = 0;+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  connect();+  if ( !data->client )+    return 0;++  // List of available ports+  const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );++  if ( ports == NULL ) return 0;+  while ( ports[count] != NULL )+    count++;++  free( ports );++  return count;+}++std::string MidiInJack :: getPortName( unsigned int portNumber )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  std::string retStr( "" );++  connect();++  // List of available ports+  const char **ports = jack_get_ports( data->client, NULL,+                                       JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );++  // Check port validity+  if ( ports == NULL ) {+    errorString_ = "MidiInJack::getPortName: no ports available!";+    error( RtMidiError::WARNING, errorString_ );+    return retStr;+  }++  unsigned int i;+  for ( i=0; i<portNumber && ports[i]; i++ ) {}+  if ( i < portNumber || !ports[portNumber] ) {+    std::ostringstream ost;+    ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::WARNING, errorString_ );+  }+  else retStr.assign( ports[portNumber] );++  jack_free( ports );+  return retStr;+}++void MidiInJack :: closePort()+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);++  if ( data->port == NULL ) return;+  jack_port_unregister( data->client, data->port );+  data->port = NULL;++  connected_ = false;+}++void MidiInJack:: setClientName( const std::string& )+{++  errorString_ = "MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiInJack :: setPortName( const std::string &portName )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+#ifdef JACK_HAS_PORT_RENAME+  jack_port_rename( data->client, data->port, portName.c_str() );+#else+  jack_port_set_name( data->port, portName.c_str() );+#endif+}++//*********************************************************************//+//  API: JACK+//  Class Definitions: MidiOutJack+//*********************************************************************//++// Jack process callback+static int jackProcessOut( jack_nframes_t nframes, void *arg )+{+  JackMidiData *data = (JackMidiData *) arg;+  jack_midi_data_t *midiData;+  int space;++  // Is port created?+  if ( data->port == NULL ) return 0;++  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 );++    jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );+  }++#ifdef HAVE_SEMAPHORE+  if ( !sem_trywait( &data->sem_needpost ) )+    sem_post( &data->sem_cleanup );+#endif++  return 0;+}++MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi()+{+  MidiOutJack::initialize( clientName );+}++void MidiOutJack :: initialize( const std::string& clientName )+{+  JackMidiData *data = new JackMidiData;+  apiData_ = (void *) data;++  data->port = NULL;+  data->client = NULL;+#ifdef HAVE_SEMAPHORE+  sem_init( &data->sem_cleanup, 0, 0 );+  sem_init( &data->sem_needpost, 0, 0 );+#endif+  this->clientName = clientName;++  connect();+}++void MidiOutJack :: connect()+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  if ( data->client )+    return;++  // Initialize output ringbuffers+  data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );+  data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );++  // Initialize JACK client+  if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) {+    errorString_ = "MidiOutJack::initialize: JACK server not running?";+    error( RtMidiError::WARNING, errorString_ );+    return;+  }++  jack_set_process_callback( data->client, jackProcessOut, data );+  jack_activate( data->client );+}++MidiOutJack :: ~MidiOutJack()+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  MidiOutJack::closePort();++  // Cleanup+  jack_ringbuffer_free( data->buffSize );+  jack_ringbuffer_free( data->buffMessage );+  if ( data->client ) {+    jack_client_close( data->client );+  }++#ifdef HAVE_SEMAPHORE+  sem_destroy( &data->sem_cleanup );+  sem_destroy( &data->sem_needpost );+#endif++  delete data;+}++void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);++  connect();++  // Creating new port+  if ( data->port == NULL )+    data->port = jack_port_register( data->client, portName.c_str(),+                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );++  if ( data->port == NULL ) {+    errorString_ = "MidiOutJack::openPort: JACK error creating port";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+    return;+  }++  // Connecting to the output+  std::string name = getPortName( portNumber );+  jack_connect( data->client, jack_port_name( data->port ), name.c_str() );++  connected_ = true;+}++void MidiOutJack :: openVirtualPort( const std::string &portName )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);++  connect();+  if ( data->port == NULL )+    data->port = jack_port_register( data->client, portName.c_str(),+                                     JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );++  if ( data->port == NULL ) {+    errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";+    error( RtMidiError::DRIVER_ERROR, errorString_ );+  }+}++unsigned int MidiOutJack :: getPortCount()+{+  int count = 0;+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  connect();+  if ( !data->client )+    return 0;++  // List of available ports+  const char **ports = jack_get_ports( data->client, NULL,+                                       JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );++  if ( ports == NULL ) return 0;+  while ( ports[count] != NULL )+    count++;++  free( ports );++  return count;+}++std::string MidiOutJack :: getPortName( unsigned int portNumber )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+  std::string retStr("");++  connect();++  // List of available ports+  const char **ports = jack_get_ports( data->client, NULL,+                                       JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );++  // Check port validity+  if ( ports == NULL ) {+    errorString_ = "MidiOutJack::getPortName: no ports available!";+    error( RtMidiError::WARNING, errorString_ );+    return retStr;+  }++  if ( ports[portNumber] == NULL ) {+    std::ostringstream ost;+    ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";+    errorString_ = ost.str();+    error( RtMidiError::WARNING, errorString_ );+  }+  else retStr.assign( ports[portNumber] );++  free( ports );+  return retStr;+}++void MidiOutJack :: closePort()+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);++  if ( data->port == NULL ) return;++#ifdef HAVE_SEMAPHORE+  struct timespec ts;+  if ( clock_gettime( CLOCK_REALTIME, &ts ) != -1 ) {+    ts.tv_sec += 1; // wait max one second+    sem_post( &data->sem_needpost );+    sem_timedwait( &data->sem_cleanup, &ts );+  }+#endif++  jack_port_unregister( data->client, data->port );+  data->port = NULL;++  connected_ = false;+}++void MidiOutJack:: setClientName( const std::string& )+{++  errorString_ = "MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API!";+  error( RtMidiError::WARNING, errorString_ );++}++void MidiOutJack :: setPortName( const std::string &portName )+{+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);+#ifdef JACK_HAS_PORT_RENAME+  jack_port_rename( data->client, data->port, portName.c_str() );+#else+  jack_port_set_name( data->port, portName.c_str() );+#endif+}++void MidiOutJack :: sendMessage( const unsigned char *message, size_t size )+{+  int nBytes = static_cast<int>(size);+  JackMidiData *data = static_cast<JackMidiData *> (apiData_);++  // Write full message to buffer+  jack_ringbuffer_write( data->buffMessage, ( const char * ) message, nBytes );   jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); } 
rtmidi/RtMidi.h view
@@ -5,10 +5,11 @@     This class implements some common functionality for the realtime     MIDI input/output subclasses RtMidiIn and RtMidiOut. -    RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/+    RtMidi GitHub site: https://github.com/thestk/rtmidi+    RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/      RtMidi: realtime MIDI i/o C++ classes-    Copyright (c) 2003-2016 Gary P. Scavone+    Copyright (c) 2003-2019 Gary P. Scavone      Permission is hereby granted, free of charge, to any person     obtaining a copy of this software and associated documentation files@@ -43,8 +44,22 @@ #ifndef RTMIDI_H #define RTMIDI_H -#define RTMIDI_VERSION "2.1.1"+#if defined _WIN32 || defined __CYGWIN__+  #if defined(RTMIDI_EXPORT)+    #define RTMIDI_DLL_PUBLIC __declspec(dllexport)+  #else+    #define RTMIDI_DLL_PUBLIC+  #endif+#else+  #if __GNUC__ >= 4+    #define RTMIDI_DLL_PUBLIC __attribute__( (visibility( "default" )) )+  #else+    #define RTMIDI_DLL_PUBLIC+  #endif+#endif +#define RTMIDI_VERSION "4.0.0"+ #include <exception> #include <iostream> #include <string>@@ -60,7 +75,7 @@ */ /************************************************************************/ -class RtMidiError : public std::exception+class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception {  public:   //! Defined RtMidiError types.@@ -79,8 +94,9 @@   };    //! The constructor.-  RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() : message_(message), type_(type) {}- +  RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw()+    : message_(message), type_(type) {}+   //! The destructor.   virtual ~RtMidiError( void ) throw() {} @@ -88,10 +104,10 @@   virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; }    //! Returns the thrown error message type.-  virtual const Type& getType(void) const throw() { return type_; }+  virtual const Type& getType( void ) const throw() { return type_; }    //! Returns the thrown error message string.-  virtual const std::string& getMessage(void) const throw() { return message_; }+  virtual const std::string& getMessage( void ) const throw() { return message_; }    //! Returns the thrown error message as a c-style string.   virtual const char* what( void ) const throw() { return message_.c_str(); }@@ -113,18 +129,18 @@  class MidiApi; -class RtMidi+class RTMIDI_DLL_PUBLIC RtMidi {  public:-   //! MIDI API specifier arguments.   enum Api {     UNSPECIFIED,    /*!< Search for a working compiled API. */-    MACOSX_CORE,    /*!< Macintosh OS-X Core Midi API. */+    MACOSX_CORE,    /*!< Macintosh OS-X CoreMIDI API. */     LINUX_ALSA,     /*!< The Advanced Linux Sound Architecture API. */     UNIX_JACK,      /*!< The JACK Low-Latency MIDI Server API. */     WINDOWS_MM,     /*!< The Microsoft Multimedia MIDI API. */-    RTMIDI_DUMMY    /*!< A compilable but non-functional API. */+    RTMIDI_DUMMY,   /*!< A compilable but non-functional API. */+    NUM_APIS        /*!< Number of values in this enum. */   };    //! A static function to determine the current RtMidi version.@@ -138,11 +154,34 @@   */   static void getCompiledApi( std::vector<RtMidi::Api> &apis ) throw(); +  //! Return the name of a specified compiled MIDI API.+  /*!+    This obtains a short lower-case name used for identification purposes.+    This value is guaranteed to remain identical across library versions.+    If the API is unknown, this function will return the empty string.+  */+  static std::string getApiName( RtMidi::Api api );++  //! Return the display name of a specified compiled MIDI API.+  /*!+    This obtains a long name used for display purposes.+    If the API is unknown, this function will return the empty string.+  */+  static std::string getApiDisplayName( RtMidi::Api api );++  //! Return the compiled MIDI API having the given name.+  /*!+    A case insensitive comparison will check the specified name+    against the list of compiled APIs, and return the one which+    matches. On failure, the function returns UNSPECIFIED.+  */+  static RtMidi::Api getCompiledApiByName( const std::string &name );+   //! Pure virtual openPort() function.-  virtual void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi" ) ) = 0;+  virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0;    //! Pure virtual openVirtualPort() function.-  virtual void openVirtualPort( const std::string portName = std::string( "RtMidi" ) ) = 0;+  virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0;    //! Pure virtual getPortCount() function.   virtual unsigned int getPortCount() = 0;@@ -153,7 +192,14 @@   //! Pure virtual closePort() function.   virtual void closePort( void ) = 0; +  void setClientName( const std::string &clientName );+  void setPortName( const std::string &portName );+   //! Returns true if a port is open and false if not.+  /*!+      Note that this only applies to connections made with the openPort()+      function, not to virtual ports.+  */   virtual bool isPortOpen( void ) const = 0;    //! Set an error callback function to be invoked when an error has occured.@@ -164,10 +210,8 @@   virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0;   protected:-   RtMidi();   virtual ~RtMidi();-   MidiApi *rtapi_; }; @@ -185,7 +229,7 @@     possible to open a virtual input port to which other MIDI software     clients can connect. -    by Gary P. Scavone, 2003-2014.+    by Gary P. Scavone, 2003-2017. */ /**********************************************************************/ @@ -203,12 +247,12 @@ // // **************************************************************** // -class RtMidiIn : public RtMidi+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);+  typedef void (*RtMidiCallback)( double timeStamp, std::vector<unsigned char> *message, void *userData );    //! Default constructor that allows an optional api, client name and queue size.   /*!@@ -229,7 +273,7 @@     \param queueSizeLimit An optional size of the MIDI input queue can be specified.   */   RtMidiIn( RtMidi::Api api=UNSPECIFIED,-            const std::string clientName = std::string( "RtMidi Input Client"),+            const std::string& clientName = "RtMidi Input Client",             unsigned int queueSizeLimit = 100 );    //! If a MIDI connection is still open, it will be closed by the destructor.@@ -244,7 +288,7 @@                       Otherwise, the default or first port found is opened.     \param portName An optional name for the application port that is used to connect to portId can be specified.   */-  void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Input" ) );+  void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) );    //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only).   /*!@@ -256,7 +300,7 @@     \param portName An optional name for the application port that is                     used to connect to portId can be specified.   */-  void openVirtualPort( const std::string portName = std::string( "RtMidi Input" ) );+  void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) );    //! Set a callback function to be invoked for incoming MIDI messages.   /*!@@ -282,6 +326,10 @@   void closePort( void );    //! Returns true if a port is open and false if not.+  /*!+      Note that this only applies to connections made with the openPort()+      function, not to virtual ports.+  */   virtual bool isPortOpen() const;    //! Return the number of available MIDI input ports.@@ -293,7 +341,8 @@   //! Return a string identifier for the specified MIDI input port number.   /*!     \return The name of the port with the given Id is returned.-    \retval An empty string is returned if an invalid port specifier is provided.+    \retval An empty string is returned if an invalid port specifier+            is provided. User code should assume a UTF-8 encoding.   */   std::string getPortName( unsigned int portNumber = 0 ); @@ -325,8 +374,7 @@   virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );   protected:-  void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit );-+  void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ); };  /**********************************************************************/@@ -341,14 +389,13 @@     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-2014.+    by Gary P. Scavone, 2003-2017. */ /**********************************************************************/ -class RtMidiOut : public RtMidi+class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi {  public:-   //! Default constructor that allows an optional client name.   /*!     An exception will be thrown if a MIDI system initialization error occurs.@@ -358,7 +405,7 @@     JACK (OS-X).   */   RtMidiOut( RtMidi::Api api=UNSPECIFIED,-             const std::string clientName = std::string( "RtMidi Output Client") );+             const std::string& clientName = "RtMidi Output Client" );    //! The destructor closes any open MIDI connections.   ~RtMidiOut( void ) throw();@@ -373,12 +420,16 @@       exception is thrown if an error occurs while attempting to make       the port connection.   */-  void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Output" ) );+  void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) );    //! Close an open MIDI connection (if one exists).   void closePort( void );    //! Returns true if a port is open and false if not.+  /*!+      Note that this only applies to connections made with the openPort()+      function, not to virtual ports.+  */   virtual bool isPortOpen() const;    //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only).@@ -390,14 +441,16 @@       An exception is thrown if an error occurs while attempting to       create the virtual port.   */-  void openVirtualPort( const std::string portName = std::string( "RtMidi Output" ) );+  void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) );    //! Return the number of available MIDI output ports.   unsigned int getPortCount( void );    //! Return a string identifier for the specified MIDI port type and number.   /*!-      An empty string is returned if an invalid port specifier is provided.+    \return The name of the port with the given Id is returned.+    \retval An empty string is returned if an invalid port specifier+            is provided. User code should assume a UTF-8 encoding.   */   std::string getPortName( unsigned int portNumber = 0 ); @@ -406,8 +459,18 @@       An exception is thrown if an error occurs during output or an       output connection was not previously established.   */-  void sendMessage( std::vector<unsigned char> *message );+  void sendMessage( const std::vector<unsigned char> *message ); +  //! Immediately send a single message out an open MIDI output port.+  /*!+      An exception is thrown if an error occurs during output or an+      output connection was not previously established.++      \param message A pointer to the MIDI message as raw bytes+      \param size    Length of the MIDI message in bytes+  */+  void sendMessage( const unsigned char *message, size_t size );+   //! Set an error callback function to be invoked when an error has occured.   /*!     The callback function will be called whenever an error has occured. It is best@@ -416,7 +479,7 @@   virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );   protected:-  void openMidiApi( RtMidi::Api api, const std::string clientName );+  void openMidiApi( RtMidi::Api api, const std::string &clientName ); };  @@ -433,16 +496,18 @@ // // **************************************************************** // -class MidiApi+class RTMIDI_DLL_PUBLIC MidiApi {  public:    MidiApi();   virtual ~MidiApi();   virtual RtMidi::Api getCurrentApi( void ) = 0;-  virtual void openPort( unsigned int portNumber, const std::string portName ) = 0;-  virtual void openVirtualPort( const std::string portName ) = 0;+  virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0;+  virtual void openVirtualPort( const std::string &portName ) = 0;   virtual void closePort( void ) = 0;+  virtual void setClientName( const std::string &clientName ) = 0;+  virtual void setPortName( const std::string &portName ) = 0;    virtual unsigned int getPortCount( void ) = 0;   virtual std::string getPortName( unsigned int portNumber ) = 0;@@ -464,7 +529,7 @@   void *errorCallbackUserData_; }; -class MidiInApi : public MidiApi+class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi {  public: @@ -477,25 +542,29 @@    // A MIDI structure used internally by the class to store incoming   // messages.  Each message represents one and only one MIDI message.-  struct MidiMessage { -    std::vector<unsigned char> bytes; +  struct MidiMessage {+    std::vector<unsigned char> bytes;++    //! Time in seconds elapsed since the previous message     double timeStamp;      // Default constructor.-  MidiMessage()-  :bytes(0), timeStamp(0.0) {}+    MidiMessage()+      : bytes(0), timeStamp(0.0) {}   };    struct MidiQueue {     unsigned int front;     unsigned int back;-    unsigned int size;     unsigned int ringSize;     MidiMessage *ring;      // Default constructor.-  MidiQueue()-  :front(0), back(0), size(0), ringSize(0) {}+    MidiQueue()+      : front(0), back(0), ringSize(0), ring(0) {}+    bool push( const MidiMessage& );+    bool pop( std::vector<unsigned char>*, double* );+    unsigned int size( unsigned int *back=0, unsigned int *front=0 );   };    // The RtMidiInData structure is used to pass private class data to@@ -513,23 +582,22 @@     bool continueSysex;      // Default constructor.-  RtMidiInData()-  : ignoreFlags(7), doInput(false), firstMessage(true),-      apiData(0), usingCallback(false), userCallback(0), userData(0),-      continueSysex(false) {}+    RtMidiInData()+      : ignoreFlags(7), doInput(false), firstMessage(true), apiData(0), usingCallback(false),+        userCallback(0), userData(0), continueSysex(false) {}   };   protected:   RtMidiInData inputData_; }; -class MidiOutApi : public MidiApi+class RTMIDI_DLL_PUBLIC MidiOutApi : public MidiApi {  public:    MidiOutApi( void );   virtual ~MidiOutApi( void );-  virtual void sendMessage( std::vector<unsigned char> *message ) = 0;+  virtual void sendMessage( const unsigned char *message, size_t size ) = 0; };  // **************************************************************** //@@ -539,225 +607,27 @@ // **************************************************************** //  inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }-inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); }-inline void RtMidiIn :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); }+inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }+inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); }-inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { ((MidiInApi *)rtapi_)->setCallback( callback, userData ); }-inline void RtMidiIn :: cancelCallback( void ) { ((MidiInApi *)rtapi_)->cancelCallback(); }+inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { static_cast<MidiInApi *>(rtapi_)->setCallback( callback, userData ); }+inline void RtMidiIn :: cancelCallback( void ) { static_cast<MidiInApi *>(rtapi_)->cancelCallback(); } inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); }-inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); }-inline double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); }+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 RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }-inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); }-inline void RtMidiOut :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); }+inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }+inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); }-inline void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) { ((MidiOutApi *)rtapi_)->sendMessage( message ); }+inline void RtMidiOut :: sendMessage( const std::vector<unsigned char> *message ) { static_cast<MidiOutApi *>(rtapi_)->sendMessage( &message->at(0), message->size() ); }+inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { static_cast<MidiOutApi *>(rtapi_)->sendMessage( message, size ); } inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); }--// **************************************************************** //-//-// MidiInApi and MidiOutApi subclass prototypes.-//-// **************************************************************** //--#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__)-  #define __RTMIDI_DUMMY__-#endif--#if defined(__MACOSX_CORE__)--class MidiInCore: public MidiInApi-{- public:-  MidiInCore( const std::string clientName, unsigned int queueSizeLimit );-  ~MidiInCore( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-- protected:-  void initialize( const std::string& clientName );-};--class MidiOutCore: public MidiOutApi-{- public:-  MidiOutCore( const std::string clientName );-  ~MidiOutCore( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-  void sendMessage( std::vector<unsigned char> *message );-- protected:-  void initialize( const std::string& clientName );-};--#endif--#if defined(__UNIX_JACK__)--class MidiInJack: public MidiInApi-{- public:-  MidiInJack( const std::string clientName, unsigned int queueSizeLimit );-  ~MidiInJack( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-- protected:-  std::string clientName;--  void connect( void );-  void initialize( const std::string& clientName );-};--class MidiOutJack: public MidiOutApi-{- public:-  MidiOutJack( const std::string clientName );-  ~MidiOutJack( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-  void sendMessage( std::vector<unsigned char> *message );-- protected:-  std::string clientName;--  void connect( void );-  void initialize( const std::string& clientName );-};--#endif--#if defined(__LINUX_ALSA__)--class MidiInAlsa: public MidiInApi-{- public:-  MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit );-  ~MidiInAlsa( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-- protected:-  void initialize( const std::string& clientName );-};--class MidiOutAlsa: public MidiOutApi-{- public:-  MidiOutAlsa( const std::string clientName );-  ~MidiOutAlsa( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-  void sendMessage( std::vector<unsigned char> *message );-- protected:-  void initialize( const std::string& clientName );-};--#endif--#if defined(__WINDOWS_MM__)--class MidiInWinMM: public MidiInApi-{- public:-  MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit );-  ~MidiInWinMM( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-- protected:-  void initialize( const std::string& clientName );-};--class MidiOutWinMM: public MidiOutApi-{- public:-  MidiOutWinMM( const std::string clientName );-  ~MidiOutWinMM( void );-  RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };-  void openPort( unsigned int portNumber, const std::string portName );-  void openVirtualPort( const std::string portName );-  void closePort( void );-  unsigned int getPortCount( void );-  std::string getPortName( unsigned int portNumber );-  void sendMessage( std::vector<unsigned char> *message );-- protected:-  void initialize( const std::string& clientName );-};--#endif--#if defined(__RTMIDI_DUMMY__)--class MidiInDummy: public MidiInApi-{- public:- MidiInDummy( const std::string /*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }-  RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }-  void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {}-  void openVirtualPort( const std::string /*portName*/ ) {}-  void closePort( void ) {}-  unsigned int getPortCount( void ) { return 0; }-  std::string getPortName( unsigned int portNumber ) { return ""; }-- protected:-  void initialize( const std::string& /*clientName*/ ) {}-};--class MidiOutDummy: public MidiOutApi-{- public:-  MidiOutDummy( const std::string /*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }-  RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }-  void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {}-  void openVirtualPort( const std::string /*portName*/ ) {}-  void closePort( void ) {}-  unsigned int getPortCount( void ) { return 0; }-  std::string getPortName( unsigned int /*portNumber*/ ) { return ""; }-  void sendMessage( std::vector<unsigned char> * /*message*/ ) {}-- protected:-  void initialize( const std::string& /*clientName*/ ) {}-};--#endif  #endif
rtmidi/rtmidi_c.cpp view
@@ -3,46 +3,87 @@ #include "rtmidi_c.h" #include "RtMidi.h" -/* misc */-int rtmidi_sizeof_rtmidi_api ()+/* Compile-time assertions that will break if the enums are changed in+ * the future without synchronizing them properly.  If you get (g++)+ * "error: ‘StaticAssert<b>::StaticAssert() [with bool b = false]’ is+ * private within this context", it means enums are not aligned. */+template<bool b> class StaticAssert { private: StaticAssert() {} };+template<> class StaticAssert<true>{ public: StaticAssert() {} };+#define ENUM_EQUAL(x,y) StaticAssert<(int)x==(int)y>()+class StaticAssertions { StaticAssertions() {+    ENUM_EQUAL( RTMIDI_API_UNSPECIFIED,     RtMidi::UNSPECIFIED );+    ENUM_EQUAL( RTMIDI_API_MACOSX_CORE,     RtMidi::MACOSX_CORE );+    ENUM_EQUAL( RTMIDI_API_LINUX_ALSA,      RtMidi::LINUX_ALSA );+    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_ERROR_WARNING,            RtMidiError::WARNING );+    ENUM_EQUAL( RTMIDI_ERROR_DEBUG_WARNING,      RtMidiError::DEBUG_WARNING );+    ENUM_EQUAL( RTMIDI_ERROR_UNSPECIFIED,        RtMidiError::UNSPECIFIED );+    ENUM_EQUAL( RTMIDI_ERROR_NO_DEVICES_FOUND,   RtMidiError::NO_DEVICES_FOUND );+    ENUM_EQUAL( RTMIDI_ERROR_INVALID_DEVICE,     RtMidiError::INVALID_DEVICE );+    ENUM_EQUAL( RTMIDI_ERROR_MEMORY_ERROR,       RtMidiError::MEMORY_ERROR );+    ENUM_EQUAL( RTMIDI_ERROR_INVALID_PARAMETER,  RtMidiError::INVALID_PARAMETER );+    ENUM_EQUAL( RTMIDI_ERROR_INVALID_USE,        RtMidiError::INVALID_USE );+    ENUM_EQUAL( RTMIDI_ERROR_DRIVER_ERROR,       RtMidiError::DRIVER_ERROR );+    ENUM_EQUAL( RTMIDI_ERROR_SYSTEM_ERROR,       RtMidiError::SYSTEM_ERROR );+    ENUM_EQUAL( RTMIDI_ERROR_THREAD_ERROR,       RtMidiError::THREAD_ERROR );+}};++class CallbackProxyUserData {-	return sizeof (RtMidiApi);-}+  public:+	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[]+extern "C" const unsigned int rtmidi_num_compiled_apis;+ /* RtMidi API */-int rtmidi_get_compiled_api (enum RtMidiApi **apis) // return length for NULL argument.+int rtmidi_get_compiled_api (enum RtMidiApi *apis, unsigned int apis_size) {-	if (!apis || !(*apis)) {-		std::vector<RtMidi::Api> *v = new std::vector<RtMidi::Api> ();-		try {-			RtMidi::getCompiledApi (*v);-			int size = v->size ();-			delete v;-			return size;-		} catch (...) {-			return -1;-		}-	} else {-		try {-			std::vector<RtMidi::Api> *v = new std::vector<RtMidi::Api> ();-			RtMidi::getCompiledApi (*v);-			for (unsigned int i = 0; i < v->size (); i++)-				(*apis) [i] = (RtMidiApi) v->at (i);-			delete v;-			return 0;-		} catch (...) {-			return -1;-		}-	}+    unsigned num = rtmidi_num_compiled_apis;+    if (apis) {+        num = (num < apis_size) ? num : apis_size;+        memcpy(apis, rtmidi_compiled_apis, num * sizeof(enum RtMidiApi));+    }+    return (int)num; } -void rtmidi_error (RtMidiPtr device, enum RtMidiErrorType type, const char* errorString)+extern "C" const char* rtmidi_api_names[][2];+const char *rtmidi_api_name(enum RtMidiApi api) {+    if (api < 0 || api >= RTMIDI_API_NUM)+        return NULL;+    return rtmidi_api_names[api][0];+}++const char *rtmidi_api_display_name(enum RtMidiApi api) {-	std::string msg = errorString;-	((MidiApi*) device->ptr)->error ((RtMidiError::Type) type, msg);+    if (api < 0 || api >= RTMIDI_API_NUM)+        return "Unknown";+    return rtmidi_api_names[api][1]; } +enum RtMidiApi rtmidi_compiled_api_by_name(const char *name) {+    RtMidi::Api api = RtMidi::UNSPECIFIED;+    if (name) {+        api = RtMidi::getCompiledApiByName(name);+    }+    return (enum RtMidiApi)api;+} +void rtmidi_error (MidiApi *api, enum RtMidiErrorType type, const char* errorString)+{+	std::string msg = errorString;+	api->error ((RtMidiError::Type) type, msg);+}+ void rtmidi_open_port (RtMidiPtr device, unsigned int portNumber, const char *portName) {     std::string name = portName;@@ -113,11 +154,13 @@         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;         wrp->ok  = false;         wrp->msg = err.what ();     }@@ -134,11 +177,13 @@         RtMidiIn* rIn = new RtMidiIn ((RtMidi::Api) api, name, queueSizeLimit);                  wrp->ptr = (void*) rIn;+        wrp->data = 0;         wrp->ok  = true;         wrp->msg = "";      } catch (const RtMidiError & err) {-        wrp->ptr = 0; +        wrp->ptr = 0;+        wrp->data = 0;         wrp->ok  = false;         wrp->msg = err.what ();     }@@ -148,6 +193,8 @@  void rtmidi_in_free (RtMidiInPtr device) {+    if (device->data)+      delete (CallbackProxyUserData*) device->data;     delete (RtMidiIn*) device->ptr;     delete device; }@@ -161,21 +208,11 @@         device->ok  = false;         device->msg = err.what (); -        return RT_MIDI_API_UNSPECIFIED;+        return RTMIDI_API_UNSPECIFIED;     } } -class CallbackProxyUserData-{-  public:-	CallbackProxyUserData (RtMidiCCallback cCallback, void *userData)-		: c_callback (cCallback), user_data (userData)-	{-	}-	RtMidiCCallback c_callback;-	void *user_data;-};-+static void callback_proxy (double timeStamp, std::vector<unsigned char> *message, void *userData) { 	CallbackProxyUserData* data = reinterpret_cast<CallbackProxyUserData*> (userData);@@ -184,13 +221,14 @@  void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void *userData) {+    device->data = (void*) new CallbackProxyUserData (callback, userData);     try {-        void *data = (void *) new CallbackProxyUserData (callback, userData);-        ((RtMidiIn*) device->ptr)->setCallback (callback_proxy, data);-    +        ((RtMidiIn*) device->ptr)->setCallback (callback_proxy, device->data);     } catch (const RtMidiError & err) {         device->ok  = false;         device->msg = err.what ();+        delete (CallbackProxyUserData*) device->data;+        device->data = 0;     } } @@ -198,7 +236,8 @@ {     try {         ((RtMidiIn*) device->ptr)->cancelCallback ();-+        delete (CallbackProxyUserData*) device->data;+        device->data = 0;     } catch (const RtMidiError & err) {         device->ok  = false;         device->msg = err.what ();@@ -211,21 +250,19 @@ }  double rtmidi_in_get_message (RtMidiInPtr device, -                              unsigned char **message, -                              size_t * size)+                              unsigned char *message,+                              size_t *size) {     try {         // FIXME: use allocator to achieve efficient buffering-        std::vector<unsigned char> *v = new std::vector<unsigned char> ();-        double ret = ((RtMidiIn*) device->ptr)->getMessage (v);-        *size = v->size ();+        std::vector<unsigned char> v;+        double ret = ((RtMidiIn*) device->ptr)->getMessage (&v); -        if (v->size () > 0) {-            *message = (unsigned char *) -                        malloc (sizeof (unsigned char *) * (int) v->size ());-            memcpy (*message, v->data (), (int) v->size ());+        if (v.size () > 0 && v.size() <= *size) {+            memcpy (message, v.data (), (int) v.size ());         }-        delete v;++        *size = v.size();         return ret;     }      catch (const RtMidiError & err) {@@ -249,11 +286,13 @@         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;         wrp->ok  = false;         wrp->msg = err.what ();     }@@ -270,11 +309,13 @@         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;         wrp->ok  = false;         wrp->msg = err.what ();     }@@ -298,18 +339,14 @@         device->ok  = false;         device->msg = err.what (); -        return RT_MIDI_API_UNSPECIFIED;+        return RTMIDI_API_UNSPECIFIED;     } }  int rtmidi_out_send_message (RtMidiOutPtr device, const unsigned char *message, int length) {     try {-        // FIXME: use allocator to achieve efficient buffering-        std::vector<unsigned char> *v = new std::vector<unsigned char> (length);-        memcpy (v->data (), message, length);-        ((RtMidiOut*) device->ptr)->sendMessage (v);-        delete v;+        ((RtMidiOut*) device->ptr)->sendMessage (message, length);         return 0;     }     catch (const RtMidiError & err) {
rtmidi/rtmidi_c.h view
@@ -1,13 +1,33 @@+/************************************************************************/+/*! \defgroup C-interface+    @{ +    \brief C interface to realtime MIDI input/output C++ classes.++    RtMidi offers a C-style interface, principally for use in binding+    RtMidi to other programming languages.  All structs, enums, and+    functions listed here have direct analogs (and simply call to)+    items in the C++ RtMidi class and its supporting classes and+    types+*/+/************************************************************************/++/*!+  \file rtmidi_c.h+ */+ #include <stdbool.h>+#include <stddef.h> #ifndef RTMIDI_C_H #define RTMIDI_C_H -#include <stdlib.h>- #if defined(RTMIDI_EXPORT)+#if defined _WIN32 || defined __CYGWIN__ #define RTMIDIAPI __declspec(dllexport) #else+#define RTMIDIAPI __attribute__((visibility("default")))+#endif+#else #define RTMIDIAPI //__declspec(dllimport) #endif @@ -15,61 +35,206 @@ extern "C" { #endif +//! \brief Wraps an RtMidi object for C function return statuses. struct RtMidiWrapper {+    //! The wrapped RtMidi object.     void* ptr;+    void* data;++    //! True when the last function call was OK.      bool  ok;++    //! If an error occured (ok != true), set to an error message.     const char* msg; }; -typedef RtMidiWrapper* RtMidiPtr;-typedef RtMidiWrapper* RtMidiInPtr;-typedef RtMidiWrapper* RtMidiOutPtr;+//! \brief Typedef for a generic RtMidi pointer.+typedef struct RtMidiWrapper* RtMidiPtr; -  enum RtMidiApi {-    RT_MIDI_API_UNSPECIFIED,    /*!< Search for a working compiled API. */-    RT_MIDI_API_MACOSX_CORE,    /*!< Macintosh OS-X Core Midi API. */-    RT_MIDI_API_LINUX_ALSA,     /*!< The Advanced Linux Sound Architecture API. */-    RT_MIDI_API_UNIX_JACK,      /*!< The Jack Low-Latency MIDI Server API. */-    RT_MIDI_API_WINDOWS_MM,     /*!< The Microsoft Multimedia MIDI API. */-    RT_MIDI_API_WINDOWS_KS,     /*!< The Microsoft Kernel Streaming MIDI API. */-    RT_MIDI_API_RTMIDI_DUMMY    /*!< A compilable but non-functional API. */-  };+//! \brief Typedef for a generic RtMidiIn pointer.+typedef struct RtMidiWrapper* RtMidiInPtr; +//! \brief Typedef for a generic RtMidiOut pointer.+typedef struct RtMidiWrapper* RtMidiOutPtr;++//! \brief MIDI API specifier arguments.  See \ref RtMidi::Api.+enum RtMidiApi {+    RTMIDI_API_UNSPECIFIED,    /*!< Search for a working compiled API. */+    RTMIDI_API_MACOSX_CORE,    /*!< Macintosh OS-X CoreMIDI API. */+    RTMIDI_API_LINUX_ALSA,     /*!< The Advanced Linux Sound Architecture API. */+    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_NUM             /*!< Number of values in this enum. */+};++//! \brief Defined RtMidiError types. See \ref RtMidiError::Type. enum RtMidiErrorType {-  RT_ERROR_WARNING, RT_ERROR_DEBUG_WARNING, RT_ERROR_UNSPECIFIED, RT_ERROR_NO_DEVICES_FOUND,-  RT_ERROR_INVALID_DEVICE, RT_ERROR_MEMORY_ERROR, RT_ERROR_INVALID_PARAMETER, RT_ERROR_INVALID_USE,-  RT_ERROR_DRIVER_ERROR, RT_ERROR_SYSTEM_ERROR, RT_ERROR_THREAD_ERROR+  RTMIDI_ERROR_WARNING,           /*!< A non-critical error. */+  RTMIDI_ERROR_DEBUG_WARNING,     /*!< A non-critical error which might be useful for debugging. */+  RTMIDI_ERROR_UNSPECIFIED,       /*!< The default, unspecified error type. */+  RTMIDI_ERROR_NO_DEVICES_FOUND,  /*!< No devices found on system. */+  RTMIDI_ERROR_INVALID_DEVICE,    /*!< An invalid device ID was specified. */+  RTMIDI_ERROR_MEMORY_ERROR,      /*!< An error occured during memory allocation. */+  RTMIDI_ERROR_INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */+  RTMIDI_ERROR_INVALID_USE,       /*!< The function was called incorrectly. */+  RTMIDI_ERROR_DRIVER_ERROR,      /*!< A system driver error occured. */+  RTMIDI_ERROR_SYSTEM_ERROR,      /*!< A system error occured. */+  RTMIDI_ERROR_THREAD_ERROR       /*!< A thread error occured. */ }; -typedef void(* RtMidiCCallback) (double timeStamp, const unsigned char* message, unsigned int msg_len, void *userData);+/*! \brief The type of a RtMidi callback function.+ *+ * \param timeStamp   The time at which the message has been received.+ * \param message     The midi message.+ * \param userData    Additional user data for the callback.+ *+ * See \ref RtMidiIn::RtMidiCallback.+ */+typedef void(* RtMidiCCallback) (double timeStamp, const unsigned char* message,+                                 size_t messageSize, void *userData); -RTMIDIAPI int rtmidi_sizeof_rtmidi_api ();  /* RtMidi API */-RTMIDIAPI int rtmidi_get_compiled_api (enum RtMidiApi **apis); // return length for NULL argument.-RTMIDIAPI void rtmidi_error (RtMidiPtr device, enum RtMidiErrorType type, const char* errorString); +/*! \brief Determine the available compiled MIDI APIs.+ *+ * If the given `apis` parameter is null, returns the number of available APIs.+ * Otherwise, fill the given apis array with the RtMidi::Api values.+ *+ * \param apis  An array or a null value.+ * \param apis_size  Number of elements pointed to by apis+ * \return number of items needed for apis array if apis==NULL, or+ *         number of items written to apis array otherwise.  A negative+ *         return value indicates an error.+ *+ * See \ref RtMidi::getCompiledApi().+*/+RTMIDIAPI int rtmidi_get_compiled_api (enum RtMidiApi *apis, unsigned int apis_size);++//! \brief Return the name of a specified compiled MIDI API.+//! See \ref RtMidi::getApiName().+RTMIDIAPI const char *rtmidi_api_name(enum RtMidiApi api);++//! \brief Return the display name of a specified compiled MIDI API.+//! See \ref RtMidi::getApiDisplayName().+RTMIDIAPI const char *rtmidi_api_display_name(enum RtMidiApi api);++//! \brief Return the compiled MIDI API having the given name.+//! See \ref RtMidi::getCompiledApiByName().+RTMIDIAPI enum RtMidiApi rtmidi_compiled_api_by_name(const char *name);++//! \internal Report an error.+RTMIDIAPI void rtmidi_error (enum RtMidiErrorType type, const char* errorString);++/*! \brief Open a MIDI port.+ *+ * \param port      Must be greater than 0+ * \param portName  Name for the application port.+ *+ * See RtMidi::openPort().+ */ 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.  + *+ * \param portName  Name for the application port.+ *+ * See RtMidi::openVirtualPort().+ */ RTMIDIAPI void rtmidi_open_virtual_port (RtMidiPtr device, const char *portName);++/*! \brief Close a MIDI connection.+ * See RtMidi::closePort().+ */ RTMIDIAPI void rtmidi_close_port (RtMidiPtr device);++/*! \brief Return the number of available MIDI ports.+ * See RtMidi::getPortCount().+ */ RTMIDIAPI unsigned int rtmidi_get_port_count (RtMidiPtr device);++/*! \brief Return a string identifier for the specified MIDI input port number.+ * See RtMidi::getPortName().+ */ RTMIDIAPI const char* rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber);  /* RtMidiIn API */-RTMIDIAPI RtMidiInPtr rtmidi_in_create_default ();++//! \brief Create a default RtMidiInPtr value, with no initialization.+RTMIDIAPI RtMidiInPtr rtmidi_in_create_default (void);++/*! \brief Create a  RtMidiInPtr value, with given api, clientName and queueSizeLimit.+ *+ *  \param api            An optional API id can be specified.+ *  \param clientName     An optional client name can be specified. This+ *                        will be used to group the ports that are created+ *                        by the application.+ *  \param queueSizeLimit An optional size of the MIDI input queue can be+ *                        specified.+ *+ * See RtMidiIn::RtMidiIn().+ */ RTMIDIAPI RtMidiInPtr rtmidi_in_create (enum RtMidiApi api, const char *clientName, unsigned int queueSizeLimit);++//! \brief Free the given RtMidiInPtr. RTMIDIAPI void rtmidi_in_free (RtMidiInPtr device);++//! \brief Returns the MIDI API specifier for the given instance of RtMidiIn.+//! See \ref RtMidiIn::getCurrentApi(). RTMIDIAPI enum RtMidiApi rtmidi_in_get_current_api (RtMidiPtr device);++//! \brief Set a callback function to be invoked for incoming MIDI messages.+//! See \ref RtMidiIn::setCallback(). RTMIDIAPI void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void *userData);++//! \brief Cancel use of the current callback function (if one exists).+//! See \ref RtMidiIn::cancelCallback(). RTMIDIAPI void rtmidi_in_cancel_callback (RtMidiInPtr device);++//! \brief Specify whether certain MIDI message types should be queued or ignored during input.+//! See \ref RtMidiIn::ignoreTypes(). RTMIDIAPI void rtmidi_in_ignore_types (RtMidiInPtr device, bool midiSysex, bool midiTime, bool midiSense);-RTMIDIAPI double rtmidi_in_get_message (RtMidiInPtr device, unsigned char **message, size_t * size); +/*! Fill the user-provided array with the data bytes for the next available+ * MIDI message in the input queue and return the event delta-time in seconds.+ *+ * \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. + *+ * See RtMidiIn::getMessage().+ */+RTMIDIAPI double rtmidi_in_get_message (RtMidiInPtr device, unsigned char *message, size_t *size);+ /* RtMidiOut API */-RTMIDIAPI RtMidiOutPtr rtmidi_out_create_default ();++//! \brief Create a default RtMidiInPtr value, with no initialization.+RTMIDIAPI RtMidiOutPtr rtmidi_out_create_default (void);++/*! \brief Create a RtMidiOutPtr value, with given and clientName.+ *+ *  \param api            An optional API id can be specified.+ *  \param clientName     An optional client name can be specified. This+ *                        will be used to group the ports that are created+ *                        by the application.+ *+ * See RtMidiOut::RtMidiOut().+ */ RTMIDIAPI RtMidiOutPtr rtmidi_out_create (enum RtMidiApi api, const char *clientName);++//! \brief Free the given RtMidiOutPtr. RTMIDIAPI void rtmidi_out_free (RtMidiOutPtr device);++//! \brief Returns the MIDI API specifier for the given instance of RtMidiOut.+//! See \ref RtMidiOut::getCurrentApi(). RTMIDIAPI enum RtMidiApi rtmidi_out_get_current_api (RtMidiPtr device);++//! \brief Immediately send a single message out an open MIDI output port.+//! See \ref RtMidiOut::sendMessage(). RTMIDIAPI int rtmidi_out_send_message (RtMidiOutPtr device, const unsigned char *message, int length);  @@ -77,3 +242,5 @@ } #endif #endif++/*! }@ */
+ test/Main.hs view
@@ -0,0 +1,51 @@+module Main (main) where++import Control.Concurrent (threadDelay)+import Control.Monad (replicateM_)+import Data.IORef (IORef, newIORef, readIORef, modifyIORef)+import Data.List (isInfixOf)+import Data.Word (Word8)+import Sound.RtMidi (closeDevice, closePort, defaultInput, defaultOutput, findPort, sendMessage, setCallback, openPort, openVirtualPort)+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.HUnit (testCase, (@?=))++incIORef :: IORef Int -> IO ()+incIORef = flip modifyIORef succ++readerCallback :: IORef Int -> Double -> [Word8] -> IO ()+readerCallback countRef _ msg = incIORef countRef++testVirtualReadWrite :: TestTree+testVirtualReadWrite = testCase "virtual read write" $ do+  let expectedCount = 3+      -- a simple note-on message+      message = [0x90, 0x51, 0x7f]+      portName = "rtmidi-test-port"+      -- 0.01 second delay in us+      delayUs = 10000+  countRef <- newIORef 0+  -- Create reader with callback+  inDev <- defaultInput+  setCallback inDev (readerCallback countRef)+  openVirtualPort inDev portName+  -- Create writer and connect to reader virtual port+  outDev <- defaultOutput+  maybePortNum <- findPort outDev (isInfixOf portName)+  let portNum = maybe (error "Could not find port") id maybePortNum+  openPort outDev portNum portName+  -- Send messages+  replicateM_ expectedCount (sendMessage outDev message)+  -- Sleep a bit to ensure message delivery+  threadDelay delayUs+  -- Close writer+  closePort outDev+  closeDevice outDev+  -- Close reader+  closePort inDev+  closeDevice inDev+  -- Verify number of messages received+  actualCount <- readIORef countRef+  actualCount @?= expectedCount++main :: IO ()+main = defaultMain (testGroup "RtMidi" [testVirtualReadWrite])