packages feed

RtMidi (empty) → 0.1.0.0

raw patch · 11 files changed

+4486/−0 lines, 11 filesdep +basesetup-changed

Dependencies added: base

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Sound.RtMidi: haskell wrapper for RtMidi+Copyright (c) 2017 kohlrabi++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.+
+ RtMidi.cabal view
@@ -0,0 +1,63 @@+name:                RtMidi+version:             0.1.0.0+description:         Haskell wrapper for RtMidi, the lightweight, cross-platform MIDI I/O library.+category:            Sound+build-type:          Simple+author:              kohlrabi+license-file:        LICENSE+homepage:            https://github.com/riottracker/RtMidi+maintainer:          kohlrabi@kohlra.biz+extra-source-files:  rtmidi/rtmidi_c.h+                     rtmidi/RtMidi.h+                     examples/playback.hs+                     examples/query.hs+                     examples/callback.hs+cabal-version:       >=1.10+license:             MIT++Flag alsa {+  Description:  Enable ALSA sequencer api+  Default:      False+}++Flag jack {+  Description:  Enable JACK api+  Default:      False+}++Flag core {+  Description:  Enable CoreMIDI api+  Default:      False+}++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.10+  default-language:    Haskell2010+  include-dirs:        rtmidi+  extra-libraries:     stdc+++  c-sources:           rtmidi/RtMidi.cpp+                       rtmidi/rtmidi_c.cpp+  if flag(alsa)+    cc-options:       -D__LINUX_ALSA__+    extra-libraries:  asound pthread+  if flag(jack)+    cc-options:       -D__UNIX_JACK__+    extra-libraries:  jack+  if flag(core)+    cc-options:       -D__MAXOSX_CORE__+    extra-libraries:  CoreMIDI CoreAudio CoreFoundation+++source-repository head+  type:     git+  location: https://github.com/riottracker/RtMidi+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Sound/RtMidi.hs view
@@ -0,0 +1,337 @@+{-# LANGUAGE ForeignFunctionInterface #-}+++-- | Interface to RtMidi+module Sound.RtMidi (+      Device+    , Error+    , ErrorType+    , Api(..)+    , compiledApis+    , openPort+    , openVirtualPort+    , closePort+    , portCount+    , portName+    , defaultInput+    , createInput+    , setCallback+    , setCallbackWithUserData+    , cancelCallback+    , ignoreTypes+    , getMessage+    , defaultOutput+    , createOutput+    , sendMessage+    , closeInput+    , closeOutput+    , currentApi+    ) where++import Control.Monad+import Foreign+import Foreign.C+import Foreign.C.String+++data Device = Input (Ptr ()) | Output (Ptr ())++device :: Device -> Ptr ()+device (Input x) = x+device (Output x) = x++toInput :: Device -> Ptr ()+toInput (Input x) = x++toOutput :: Device -> Ptr ()+toOutput (Output x) = x++data Api+  = UnspecifiedApi+  | CoreMidiApi+  | AlsaApi+  | JackApi+  | MultimediaApi+  | KernelStreamingApi+  | DummyApi+  deriving (Eq, Show)++instance Enum Api where+  fromEnum UnspecifiedApi = 0+  fromEnum CoreMidiApi = 1+  fromEnum AlsaApi = 2+  fromEnum JackApi = 3+  fromEnum MultimediaApi = 4+  fromEnum DummyApi = 5+  toEnum 0 = UnspecifiedApi+  toEnum 1 = CoreMidiApi+  toEnum 2 = AlsaApi+  toEnum 3 = JackApi+  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 :: String+             }+++peekWrapper :: Ptr () -> IO Wrapper+peekWrapper p = do+  a <- peek (castPtr p)+  b <- peek $ castPtr (plusPtr p $ sizeOf a)+  c <- peekCString $ castPtr (plusPtr p $ sizeOf a + sizeOf b)+  return $ Wrapper a b c++checkForErrors :: Device -> IO (Maybe String)+checkForErrors d = peekWrapper (device d) >>= \w -> if ok w then return Nothing else return $ Just (msg w)++ +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_error"+--    rtmidi_error :: CInt -> CString -> IO ()+++foreign import ccall "rtmidi_c.h rtmidi_open_port"+   rtmidi_open_port :: Ptr () -> CInt -> CString -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_open_virtual_port"+   rtmidi_open_virtual_port :: Ptr () -> CString -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_close_port"+   rtmidi_close_port :: Ptr () -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_get_port_count"+   rtmidi_get_port_count :: Ptr () -> IO CInt++foreign import ccall "rtmidi_c.h rtmidi_get_port_name"+   rtmidi_get_port_name :: Ptr () -> CInt -> IO CString+++foreign import ccall "rtmidi_c.h rtmidi_in_create_default"+   rtmidi_in_create_default :: IO (Ptr ())++foreign import ccall "rtmidi_c.h rtmidi_in_create"+   rtmidi_in_create :: CInt -> CString -> CInt -> IO (Ptr ())++foreign import ccall "rtmidi_c.h rtmidi_in_free"+   rtmidi_in_free :: Ptr () -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_in_get_current_api"+   rtmidi_in_get_current_api :: Ptr () -> IO CInt++foreign import ccall "rtmidi_c.h rtmidi_in_set_callback"+   rtmidi_in_set_callback :: Ptr () -> FunPtr (CDouble -> Ptr CUChar -> CInt -> Ptr () -> IO ()) -> Ptr () -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_in_cancel_callback"+   rtmidi_in_cancel_callback :: Ptr () -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_in_ignore_types"+   rtmidi_in_ignore_types :: Ptr () -> Bool -> Bool -> Bool -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_in_get_message"+   rtmidi_in_get_message :: Ptr () -> Ptr (Ptr CUChar) -> Ptr CSize -> IO CDouble++foreign import ccall "rtmidi_c.h rtmidi_out_create_default"+   rtmidi_out_create_default :: IO (Ptr ())++foreign import ccall "rtmidi_c.h rtmidi_out_create"+   rtmidi_out_create :: CInt -> CString -> IO (Ptr ())++foreign import ccall "rtmidi_c.h rtmidi_out_free"+   rtmidi_out_free :: Ptr () -> IO ()++foreign import ccall "rtmidi_c.h rtmidi_out_get_current_api"+   rtmidi_out_get_current_api :: Ptr () -> IO CInt++foreign import ccall "rtmidi_c.h rtmidi_out_send_message"+   rtmidi_out_send_message :: Ptr () -> Ptr CUChar -> CInt -> IO CInt+++apiSize :: IO Int+apiSize = fromEnum <$> rtmidi_sizeof_rtmidi_api++-- |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++-- -- |Report an error+-- reportError :: Error -> IO ()+-- reportError (Error e s) = withCString s $ rtmidi_error (toEnum $ fromEnum $ e)++-- |Open a MIDI connection+openPort :: Device+         -> 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)++-- |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)++-- |Close an open MIDI connection (if one exists).+closePort :: Device -> IO ()+closePort d = rtmidi_close_port $ device d++-- |Return the number of MIDI ports available to the 'Device'.+portCount :: Device -> IO Int+portCount d = fromIntegral <$> (rtmidi_get_port_count $ device d)++-- |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)++-- |Default constructor for a 'Device' to use for input.+defaultInput :: IO Device+defaultInput = Input <$> rtmidi_in_create_default++-- |Create a new 'Device' to use for input.+createInput :: 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))++foreign import ccall "wrapper"+  wrap :: (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++-- |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)++-- |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)++-- |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+            -> Bool       -- ^ SysEx messages+            -> Bool       -- ^ Time messages+            -> Bool       -- ^ Sense messages+            -> IO ()+ignoreTypes d sysex time sense = rtmidi_in_ignore_types (toInput d) sysex time sense++-- |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)++-- |Default constructor for a 'Device' to use for output.+defaultOutput :: IO Device+defaultOutput = Output <$> rtmidi_out_create_default++-- |Create a new 'Device' to use for output.+createOutput :: 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++-- |Close any open MIDI connections+closeOutput (Output x) = rtmidi_out_free x++-- |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+
+ examples/callback.hs view
@@ -0,0 +1,13 @@+import Sound.RtMidi+import Numeric+import Foreign.C++callback :: CDouble -> [CUChar] -> IO ()+callback delta msg = putStrLn $ (foldr (showHex . fromEnum) "" msg) ++ " - " ++ (show delta)++main = do+  i <- defaultInput+  openPort i 0 "RtMidi"+  setCallback i callback+  _ <- getLine+  return ()
+ examples/playback.hs view
@@ -0,0 +1,20 @@+import Sound.RtMidi+import Control.Concurrent++main = do+  device <- defaultOutput+  numPorts <- portCount device+  ports <- mapM (portName device) [0..numPorts-1]+  mapM_ (\t -> putStrLn $ show t) $ zip [0..] ports+  putStrLn "select port: "+  selection <- getLine+  openPort device (read selection) "RtMidi"+  let arp0 = take 12 $ cycle [0x51, 0x55, 0x58]+  let arp1 = take 12 $ cycle [0x51, 0x56, 0x5a]+  let arp2 = take 12 $ cycle [0x50, 0x53, 0x58]+  let song = cycle (arp0 ++ arp1 ++ arp2 ++ arp0)+  putStrLn "playing..."+  mapM_ (\x -> sendMessage device [0x90, x, 0x7f] >> threadDelay 120000) $ take 240 song+  putStrLn "exiting..."+  closePort device+  closeOutput device
+ examples/query.hs view
@@ -0,0 +1,14 @@+import Sound.RtMidi+import Control.Concurrent++main = do+  device <- createOutput AlsaApi "test"+  api <- currentApi device+  builtin <- compiledApis+  numPorts <- portCount device+  portNames <- mapM (portName device) [0..numPorts-1]+  putStrLn $ "RtMidi output using " ++ (show api)+  putStrLn $ "built-in: " ++ (show builtin)+  putStrLn $ "available Ports: " ++ (show numPorts)+  putStrLn (show portNames)+  closeOutput device
+ rtmidi/RtMidi.cpp view
@@ -0,0 +1,2843 @@+/**********************************************************************/+/*! \class RtMidi+    \brief An abstract base class for realtime MIDI input/output.++    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() );+  jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );+}++#endif  // __UNIX_JACK__
+ rtmidi/RtMidi.h view
@@ -0,0 +1,763 @@+/**********************************************************************/+/*! \class RtMidi+    \brief An abstract base class for realtime MIDI input/output.++    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.+*/+/**********************************************************************/++/*!+  \file RtMidi.h+ */++#ifndef RTMIDI_H+#define RTMIDI_H++#define RTMIDI_VERSION "2.1.1"++#include <exception>+#include <iostream>+#include <string>+#include <vector>++/************************************************************************/+/*! \class RtMidiError+    \brief Exception handling class for RtMidi.++    The RtMidiError class is quite simple but it does allow errors to be+    "caught" by RtMidiError::Type. See the RtMidi documentation to know+    which methods can throw an RtMidiError.+*/+/************************************************************************/++class RtMidiError : public std::exception+{+ public:+  //! Defined RtMidiError types.+  enum Type {+    WARNING,           /*!< A non-critical error. */+    DEBUG_WARNING,     /*!< A non-critical error which might be useful for debugging. */+    UNSPECIFIED,       /*!< The default, unspecified error type. */+    NO_DEVICES_FOUND,  /*!< No devices found on system. */+    INVALID_DEVICE,    /*!< An invalid device ID was specified. */+    MEMORY_ERROR,      /*!< An error occured during memory allocation. */+    INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */+    INVALID_USE,       /*!< The function was called incorrectly. */+    DRIVER_ERROR,      /*!< A system driver error occured. */+    SYSTEM_ERROR,      /*!< A system error occured. */+    THREAD_ERROR       /*!< A thread error occured. */+  };++  //! The constructor.+  RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() : message_(message), type_(type) {}+ +  //! The destructor.+  virtual ~RtMidiError( void ) throw() {}++  //! Prints thrown error message to stderr.+  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_; }++  //! Returns the thrown error message string.+  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(); }++ protected:+  std::string message_;+  Type type_;+};++//! RtMidi error callback function prototype.+/*!+    \param type Type of error.+    \param errorText Error description.++    Note that class behaviour is undefined after a critical error (not+    a warning) is reported.+ */+typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData );++class MidiApi;++class RtMidi+{+ public:++  //! MIDI API specifier arguments.+  enum Api {+    UNSPECIFIED,    /*!< Search for a working compiled API. */+    MACOSX_CORE,    /*!< Macintosh OS-X Core Midi 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. */+  };++  //! A static function to determine the current RtMidi version.+  static std::string getVersion( void ) throw();++  //! A static function to determine the available compiled MIDI APIs.+  /*!+    The values returned in the std::vector can be compared against+    the enumerated list values.  Note that there can be more than one+    API compiled for certain operating systems.+  */+  static void getCompiledApi( std::vector<RtMidi::Api> &apis ) throw();++  //! Pure virtual openPort() function.+  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;++  //! Pure virtual getPortCount() function.+  virtual unsigned int getPortCount() = 0;++  //! Pure virtual getPortName() function.+  virtual std::string getPortName( unsigned int portNumber = 0 ) = 0;++  //! Pure virtual closePort() function.+  virtual void closePort( void ) = 0;++  //! Returns true if a port is open and false if not.+  virtual bool isPortOpen( void ) const = 0;++  //! 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+    to set the error callback function before opening a port.+  */+  virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0;++ protected:++  RtMidi();+  virtual ~RtMidi();++  MidiApi *rtapi_;+};++/**********************************************************************/+/*! \class RtMidiIn+    \brief A realtime MIDI input class.++    This class provides a common, platform-independent API for+    realtime MIDI input.  It allows access to a single MIDI input+    port.  Incoming MIDI messages are either saved to a queue for+    retrieval using the getMessage() function or immediately passed to+    a user-specified callback function.  Create multiple instances of+    this class to connect to more than one MIDI device at the same+    time.  With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also+    possible to open a virtual input port to which other MIDI software+    clients can connect.++    by Gary P. Scavone, 2003-2014.+*/+/**********************************************************************/++// **************************************************************** //+//+// RtMidiIn and RtMidiOut class declarations.+//+// RtMidiIn / RtMidiOut are "controllers" used to select an available+// MIDI input or output interface.  They present common APIs for the+// user to call but all functionality is implemented by the classes+// MidiInApi, MidiOutApi and their subclasses.  RtMidiIn and RtMidiOut+// each create an instance of a MidiInApi or MidiOutApi subclass based+// on the user's API choice.  If no choice is made, they attempt to+// make a "logical" API selection.+//+// **************************************************************** //++class RtMidiIn : public RtMidi+{+ public:++  //! User callback function type definition.+  typedef void (*RtMidiCallback)( double timeStamp, std::vector<unsigned char> *message, void *userData);++  //! Default constructor that allows an optional api, client name and queue size.+  /*!+    An exception will be thrown if a MIDI system initialization+    error occurs.  The queue size defines the maximum number of+    messages that can be held in the MIDI queue (when not using a+    callback function).  If the queue size limit is reached,+    incoming messages will be ignored.++    If no API argument is specified and multiple API support has been+    compiled, the default order of use is ALSA, JACK (Linux) and CORE,+    JACK (OS-X).++    \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.+  */+  RtMidiIn( RtMidi::Api api=UNSPECIFIED,+            const std::string clientName = std::string( "RtMidi Input Client"),+            unsigned int queueSizeLimit = 100 );++  //! If a MIDI connection is still open, it will be closed by the destructor.+  ~RtMidiIn ( void ) throw();++  //! Returns the MIDI API specifier for the current instance of RtMidiIn.+  RtMidi::Api getCurrentApi( void ) throw();++  //! Open a MIDI input connection given by enumeration number.+  /*!+    \param portNumber An optional port number greater than 0 can be specified.+                      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" ) );++  //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only).+  /*!+    This function creates a virtual MIDI input port to which other+    software applications can connect.  This type of functionality+    is currently only supported by the Macintosh OS-X, any JACK,+    and Linux ALSA APIs (the function returns an error for the other APIs).++    \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" ) );++  //! 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.++    \param callback A callback function must be given.+    \param userData Optionally, a pointer to additional data can be+                    passed to the callback function whenever it is called.+  */+  void setCallback( RtMidiCallback callback, void *userData = 0 );++  //! 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 \e getMessage function.+  */+  void cancelCallback();++  //! Close an open MIDI connection (if one exists).+  void closePort( void );++  //! Returns true if a port is open and false if not.+  virtual bool isPortOpen() const;++  //! Return the number of available MIDI input ports.+  /*!+    \return This function returns the number of MIDI ports of the selected API.+  */+  unsigned int getPortCount();++  //! 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.+  */+  std::string getPortName( unsigned int portNumber = 0 );++  //! 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.+  */+  void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true );++  //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds.+  /*!+    This function returns immediately whether a new message is+    available or not.  A valid message is indicated by a non-zero+    vector size.  An exception is thrown if an error occurs during+    message retrieval or an input connection was not previously+    established.+  */+  double getMessage( std::vector<unsigned char> *message );++  //! 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+    to set the error callback function before opening a port.+  */+  virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );++ protected:+  void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit );++};++/**********************************************************************/+/*! \class RtMidiOut+    \brief A realtime MIDI output class.++    This class provides a common, platform-independent API for MIDI+    output.  It allows one to probe available MIDI output ports, to+    connect to one such port, and to send MIDI bytes immediately over+    the connection.  Create multiple instances of this class to+    connect to more than one MIDI device at the same time.  With the+    OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a+    virtual port to which other MIDI software clients can connect.++    by Gary P. Scavone, 2003-2014.+*/+/**********************************************************************/++class RtMidiOut : public RtMidi+{+ public:++  //! Default constructor that allows an optional client name.+  /*!+    An exception will be thrown if a MIDI system initialization error occurs.++    If no API argument is specified and multiple API support has been+    compiled, the default order of use is ALSA, JACK (Linux) and CORE,+    JACK (OS-X).+  */+  RtMidiOut( RtMidi::Api api=UNSPECIFIED,+             const std::string clientName = std::string( "RtMidi Output Client") );++  //! The destructor closes any open MIDI connections.+  ~RtMidiOut( void ) throw();++  //! Returns the MIDI API specifier for the current instance of RtMidiOut.+  RtMidi::Api getCurrentApi( void ) throw();++  //! Open a MIDI output connection.+  /*!+      An optional port number greater than 0 can be specified.+      Otherwise, the default or first port found is opened.  An+      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" ) );++  //! Close an open MIDI connection (if one exists).+  void closePort( void );++  //! Returns true if a port is open and false if not.+  virtual bool isPortOpen() const;++  //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only).+  /*!+      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).+      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" ) );++  //! 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.+  */+  std::string getPortName( unsigned int portNumber = 0 );++  //! 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.+  */+  void sendMessage( std::vector<unsigned char> *message );++  //! 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+    to set the error callback function before opening a port.+  */+  virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );++ protected:+  void openMidiApi( RtMidi::Api api, const std::string clientName );+};+++// **************************************************************** //+//+// MidiInApi / MidiOutApi class declarations.+//+// Subclasses of MidiInApi and MidiOutApi contain all API- and+// OS-specific code necessary to fully implement the RtMidi API.+//+// Note that MidiInApi and MidiOutApi are abstract base classes and+// cannot be explicitly instantiated.  RtMidiIn and RtMidiOut will+// create instances of a MidiInApi or MidiOutApi subclass.+//+// **************************************************************** //++class 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 closePort( void ) = 0;++  virtual unsigned int getPortCount( void ) = 0;+  virtual std::string getPortName( unsigned int portNumber ) = 0;++  inline bool isPortOpen() const { return connected_; }+  void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData );++  //! A basic error reporting function for RtMidi classes.+  void error( RtMidiError::Type type, std::string errorString );++protected:+  virtual void initialize( const std::string& clientName ) = 0;++  void *apiData_;+  bool connected_;+  std::string errorString_;+  RtMidiErrorCallback errorCallback_;+  bool firstErrorOccurred_;+  void *errorCallbackUserData_;+};++class MidiInApi : public MidiApi+{+ public:++  MidiInApi( unsigned int queueSizeLimit );+  virtual ~MidiInApi( void );+  void setCallback( RtMidiIn::RtMidiCallback callback, void *userData );+  void cancelCallback( void );+  virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense );+  double getMessage( std::vector<unsigned char> *message );++  // 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; +    double timeStamp;++    // Default constructor.+  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) {}+  };++  // The RtMidiInData structure is used to pass private class data to+  // the MIDI input handling function or thread.+  struct RtMidiInData {+    MidiQueue queue;+    MidiMessage message;+    unsigned char ignoreFlags;+    bool doInput;+    bool firstMessage;+    void *apiData;+    bool usingCallback;+    RtMidiIn::RtMidiCallback userCallback;+    void *userData;+    bool continueSysex;++    // Default constructor.+  RtMidiInData()+  : ignoreFlags(7), doInput(false), firstMessage(true),+      apiData(0), usingCallback(false), userCallback(0), userData(0),+      continueSysex(false) {}+  };++ protected:+  RtMidiInData inputData_;+};++class MidiOutApi : public MidiApi+{+ public:++  MidiOutApi( void );+  virtual ~MidiOutApi( void );+  virtual void sendMessage( std::vector<unsigned char> *message ) = 0;+};++// **************************************************************** //+//+// Inline RtMidiIn and RtMidiOut definitions.+//+// **************************************************************** //++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 :: 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 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 :: 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 :: 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 :: 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
@@ -0,0 +1,324 @@+#include <string.h>+#include <stdlib.h>+#include "rtmidi_c.h"+#include "RtMidi.h"++/* misc */+int rtmidi_sizeof_rtmidi_api ()+{+	return sizeof (RtMidiApi);+}++/* RtMidi API */+int rtmidi_get_compiled_api (enum RtMidiApi **apis) // return length for NULL argument.+{+	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;+		}+	}+}++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;+    try {+        ((RtMidi*) device->ptr)->openPort (portNumber, name);+    +    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+    }+}++void rtmidi_open_virtual_port (RtMidiPtr device, const char *portName)+{+    std::string name = portName;+    try {+        ((RtMidi*) device->ptr)->openVirtualPort (name);+    +    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+    }++}++void rtmidi_close_port (RtMidiPtr device)+{+    try { +        ((RtMidi*) device->ptr)->closePort ();++    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+    }+}++unsigned int rtmidi_get_port_count (RtMidiPtr device)+{+    try {+        return ((RtMidi*) device->ptr)->getPortCount ();++    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+        return -1;+    }+}++const char* rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber)+{+    try {+        std::string name = ((RtMidi*) device->ptr)->getPortName (portNumber);+        return strdup (name.c_str ());+    +    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+        return "";+    }+}++/* RtMidiIn API */+RtMidiInPtr rtmidi_in_create_default ()+{+    RtMidiWrapper* wrp = new RtMidiWrapper;+    +    try {+        RtMidiIn* rIn = new RtMidiIn ();+        +        wrp->ptr = (void*) rIn;+        wrp->ok  = true;+        wrp->msg = "";+    +    } catch (const RtMidiError & err) {+        wrp->ptr = 0;+        wrp->ok  = false;+        wrp->msg = err.what ();+    }++    return wrp;+}++RtMidiInPtr rtmidi_in_create (enum RtMidiApi api, const char *clientName, unsigned int queueSizeLimit)+{+    std::string name = clientName;+    RtMidiWrapper* wrp = new RtMidiWrapper;+    +    try {+        RtMidiIn* rIn = new RtMidiIn ((RtMidi::Api) api, name, queueSizeLimit);+        +        wrp->ptr = (void*) rIn;+        wrp->ok  = true;+        wrp->msg = "";++    } catch (const RtMidiError & err) {+        wrp->ptr = 0; +        wrp->ok  = false;+        wrp->msg = err.what ();+    }++    return wrp;+}++void rtmidi_in_free (RtMidiInPtr device)+{+    delete (RtMidiIn*) device->ptr;+    delete device;+}++enum RtMidiApi rtmidi_in_get_current_api (RtMidiPtr device)+{+    try {+        return (RtMidiApi) ((RtMidiIn*) device->ptr)->getCurrentApi ();+    +    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();++        return RT_MIDI_API_UNSPECIFIED;+    }+}++class CallbackProxyUserData+{+  public:+	CallbackProxyUserData (RtMidiCCallback cCallback, void *userData)+		: c_callback (cCallback), user_data (userData)+	{+	}+	RtMidiCCallback c_callback;+	void *user_data;+};++void callback_proxy (double timeStamp, std::vector<unsigned char> *message, void *userData)+{+	CallbackProxyUserData* data = reinterpret_cast<CallbackProxyUserData*> (userData);+	data->c_callback (timeStamp, message->data (), message->size (), data->user_data);+}++void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void *userData)+{+    try {+        void *data = (void *) new CallbackProxyUserData (callback, userData);+        ((RtMidiIn*) device->ptr)->setCallback (callback_proxy, data);+    +    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+    }+}++void rtmidi_in_cancel_callback (RtMidiInPtr device)+{+    try {+        ((RtMidiIn*) device->ptr)->cancelCallback ();++    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+    }+}++void rtmidi_in_ignore_types (RtMidiInPtr device, bool midiSysex, bool midiTime, bool midiSense)+{+	((RtMidiIn*) device->ptr)->ignoreTypes (midiSysex, midiTime, midiSense);+}++double rtmidi_in_get_message (RtMidiInPtr device, +                              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 ();++        if (v->size () > 0) {+            *message = (unsigned char *) +                        malloc (sizeof (unsigned char *) * (int) v->size ());+            memcpy (*message, v->data (), (int) v->size ());+        }+        delete v;+        return ret;+    } +    catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+        return -1;+    }+    catch (...) {+        device->ok  = false;+        device->msg = "Unknown error";+        return -1;+    }+}++/* RtMidiOut API */+RtMidiOutPtr rtmidi_out_create_default ()+{+    RtMidiWrapper* wrp = new RtMidiWrapper;++    try {+        RtMidiOut* rOut = new RtMidiOut ();+        +        wrp->ptr = (void*) rOut;+        wrp->ok  = true;+        wrp->msg = "";+    +    } catch (const RtMidiError & err) {+        wrp->ptr = 0;+        wrp->ok  = false;+        wrp->msg = err.what ();+    }++    return wrp;+}++RtMidiOutPtr rtmidi_out_create (enum RtMidiApi api, const char *clientName)+{+    RtMidiWrapper* wrp = new RtMidiWrapper;+    std::string name = clientName;++    try {+        RtMidiOut* rOut = new RtMidiOut ((RtMidi::Api) api, name);+        +        wrp->ptr = (void*) rOut;+        wrp->ok  = true;+        wrp->msg = "";+    +    } catch (const RtMidiError & err) {+        wrp->ptr = 0;+        wrp->ok  = false;+        wrp->msg = err.what ();+    }+++    return wrp;+}++void rtmidi_out_free (RtMidiOutPtr device)+{+    delete (RtMidiOut*) device->ptr;+    delete device;+}++enum RtMidiApi rtmidi_out_get_current_api (RtMidiPtr device)+{+    try {+        return (RtMidiApi) ((RtMidiOut*) device->ptr)->getCurrentApi ();++    } catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();++        return RT_MIDI_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;+        return 0;+    }+    catch (const RtMidiError & err) {+        device->ok  = false;+        device->msg = err.what ();+        return -1;+    }+    catch (...) {+        device->ok  = false;+        device->msg = "Unknown error";+        return -1;+    }+}
+ rtmidi/rtmidi_c.h view
@@ -0,0 +1,77 @@++#include <stdbool.h>+#ifndef RTMIDI_C_H+#define RTMIDI_C_H++#if defined(RTMIDI_EXPORT)+#define RTMIDIAPI __declspec(dllexport)+#else+#define RTMIDIAPI //__declspec(dllimport)+#endif++#ifdef __cplusplus+extern "C" {+#endif++struct RtMidiWrapper {+    void* ptr;+    bool  ok;+    const char* msg;+};++typedef RtMidiWrapper* RtMidiPtr;+typedef RtMidiWrapper* RtMidiInPtr;+typedef RtMidiWrapper* RtMidiOutPtr;++  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. */+  };++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+};++typedef void(* RtMidiCCallback) (double timeStamp, const unsigned char* message, unsigned int msg_len, 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 (enum RtMidiErrorType type, const char* errorString);++RTMIDIAPI void rtmidi_open_port (RtMidiPtr device, unsigned int portNumber, const char *portName);+RTMIDIAPI void rtmidi_open_virtual_port (RtMidiPtr device, const char *portName);+RTMIDIAPI void rtmidi_close_port (RtMidiPtr device);+RTMIDIAPI unsigned int rtmidi_get_port_count (RtMidiPtr device);+RTMIDIAPI const char* rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber);++/* RtMidiIn API */+RTMIDIAPI RtMidiInPtr rtmidi_in_create_default ();+RTMIDIAPI RtMidiInPtr rtmidi_in_create (enum RtMidiApi api, const char *clientName, unsigned int queueSizeLimit);+RTMIDIAPI void rtmidi_in_free (RtMidiInPtr device);+RTMIDIAPI enum RtMidiApi rtmidi_in_get_current_api (RtMidiPtr device);+RTMIDIAPI void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void *userData);+RTMIDIAPI void rtmidi_in_cancel_callback (RtMidiInPtr device);+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);++/* RtMidiOut API */+RTMIDIAPI RtMidiOutPtr rtmidi_out_create_default ();+RTMIDIAPI RtMidiOutPtr rtmidi_out_create (enum RtMidiApi api, const char *clientName);+RTMIDIAPI void rtmidi_out_free (RtMidiOutPtr device);+RTMIDIAPI enum RtMidiApi rtmidi_out_get_current_api (RtMidiPtr device);+RTMIDIAPI int rtmidi_out_send_message (RtMidiOutPtr device, const unsigned char *message, int length);+++#ifdef __cplusplus+}+#endif+#endif