RtMidi (empty) → 0.1.0.0
raw patch · 11 files changed
+4486/−0 lines, 11 filesdep +basesetup-changed
Dependencies added: base
Files
- LICENSE +30/−0
- RtMidi.cabal +63/−0
- Setup.hs +2/−0
- Sound/RtMidi.hs +337/−0
- examples/callback.hs +13/−0
- examples/playback.hs +20/−0
- examples/query.hs +14/−0
- rtmidi/RtMidi.cpp +2843/−0
- rtmidi/RtMidi.h +763/−0
- rtmidi/rtmidi_c.cpp +324/−0
- rtmidi/rtmidi_c.h +77/−0
+ 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