DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

RtMidi.cpp (126409B)


      1 /**********************************************************************/
      2 /*! \class RtMidi
      3     \brief An abstract base class for realtime MIDI input/output.
      4 
      5     This class implements some common functionality for the realtime
      6     MIDI input/output subclasses RtMidiIn and RtMidiOut.
      7 
      8     RtMidi GitHub site: https://github.com/thestk/rtmidi
      9     RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/
     10 
     11     RtMidi: realtime MIDI i/o C++ classes
     12     Copyright (c) 2003-2021 Gary P. Scavone
     13 
     14     Permission is hereby granted, free of charge, to any person
     15     obtaining a copy of this software and associated documentation files
     16     (the "Software"), to deal in the Software without restriction,
     17     including without limitation the rights to use, copy, modify, merge,
     18     publish, distribute, sublicense, and/or sell copies of the Software,
     19     and to permit persons to whom the Software is furnished to do so,
     20     subject to the following conditions:
     21 
     22     The above copyright notice and this permission notice shall be
     23     included in all copies or substantial portions of the Software.
     24 
     25     Any person wishing to distribute modifications to the Software is
     26     asked to send the modifications to the original developer so that
     27     they can be incorporated into the canonical version.  This is,
     28     however, not a binding provision of this license.
     29 
     30     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     31     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     32     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     33     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
     34     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
     35     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     36     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     37 */
     38 /**********************************************************************/
     39 
     40 #include "RtMidi.h"
     41 #include <sstream>
     42 #if defined(__APPLE__)
     43 #include <TargetConditionals.h>
     44 #endif
     45 
     46 #if (TARGET_OS_IPHONE == 1)
     47 
     48     #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
     49     #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
     50 
     51     #include <mach/mach_time.h>
     52     class CTime2nsFactor
     53     {
     54     public:
     55         CTime2nsFactor()
     56         {
     57             mach_timebase_info_data_t tinfo;
     58             mach_timebase_info(&tinfo);
     59             Factor = (double)tinfo.numer / tinfo.denom;
     60         }
     61         static double Factor;
     62     };
     63     double CTime2nsFactor::Factor;
     64     static CTime2nsFactor InitTime2nsFactor;
     65     #undef AudioGetCurrentHostTime
     66     #undef AudioConvertHostTimeToNanos
     67   #define AudioGetCurrentHostTime (uint64_t) mach_absolute_time
     68   #define AudioConvertHostTimeToNanos(t) t *CTime2nsFactor::Factor
     69   #define EndianS32_BtoN(n) n
     70 
     71 #endif
     72 
     73 // Default for Windows is to add an identifier to the port names; this
     74 // flag can be defined (e.g. in your project file) to disable this behaviour.
     75 //#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
     76 
     77 // **************************************************************** //
     78 //
     79 // MidiInApi and MidiOutApi subclass prototypes.
     80 //
     81 // **************************************************************** //
     82 
     83 #if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(TARGET_IPHONE_OS) && !defined(__WEB_MIDI_API__)
     84   #define __RTMIDI_DUMMY__
     85 #endif
     86 
     87 #if defined(__MACOSX_CORE__)
     88 #include <CoreMIDI/CoreMIDI.h>
     89 
     90 class MidiInCore: public MidiInApi
     91 {
     92  public:
     93   MidiInCore( const std::string &clientName, unsigned int queueSizeLimit );
     94   ~MidiInCore( void );
     95   RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };
     96   void openPort( unsigned int portNumber, const std::string &portName );
     97   void openVirtualPort( const std::string &portName );
     98   void closePort( void );
     99   void setClientName( const std::string &clientName );
    100   void setPortName( const std::string &portName );
    101   unsigned int getPortCount( void );
    102   std::string getPortName( unsigned int portNumber );
    103 
    104  protected:
    105   MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
    106   void initialize( const std::string& clientName );
    107 };
    108 
    109 class MidiOutCore: public MidiOutApi
    110 {
    111  public:
    112   MidiOutCore( const std::string &clientName );
    113   ~MidiOutCore( void );
    114   RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };
    115   void openPort( unsigned int portNumber, const std::string &portName );
    116   void openVirtualPort( const std::string &portName );
    117   void closePort( void );
    118   void setClientName( const std::string &clientName );
    119   void setPortName( const std::string &portName );
    120   unsigned int getPortCount( void );
    121   std::string getPortName( unsigned int portNumber );
    122   void sendMessage( const unsigned char *message, size_t size );
    123 
    124  protected:
    125   MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
    126   void initialize( const std::string& clientName );
    127 };
    128 
    129 #endif
    130 
    131 #if defined(__UNIX_JACK__)
    132 
    133 class MidiInJack: public MidiInApi
    134 {
    135  public:
    136   MidiInJack( const std::string &clientName, unsigned int queueSizeLimit );
    137   ~MidiInJack( void );
    138   RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };
    139   void openPort( unsigned int portNumber, const std::string &portName );
    140   void openVirtualPort( const std::string &portName );
    141   void closePort( void );
    142   void setClientName( const std::string &clientName );
    143   void setPortName( const std::string &portName);
    144   unsigned int getPortCount( void );
    145   std::string getPortName( unsigned int portNumber );
    146 
    147  protected:
    148   std::string clientName;
    149 
    150   void connect( void );
    151   void initialize( const std::string& clientName );
    152 };
    153 
    154 class MidiOutJack: public MidiOutApi
    155 {
    156  public:
    157   MidiOutJack( const std::string &clientName );
    158   ~MidiOutJack( void );
    159   RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };
    160   void openPort( unsigned int portNumber, const std::string &portName );
    161   void openVirtualPort( const std::string &portName );
    162   void closePort( void );
    163   void setClientName( const std::string &clientName );
    164   void setPortName( const std::string &portName);
    165   unsigned int getPortCount( void );
    166   std::string getPortName( unsigned int portNumber );
    167   void sendMessage( const unsigned char *message, size_t size );
    168 
    169  protected:
    170   std::string clientName;
    171 
    172   void connect( void );
    173   void initialize( const std::string& clientName );
    174 };
    175 
    176 #endif
    177 
    178 #if defined(__LINUX_ALSA__)
    179 
    180 class MidiInAlsa: public MidiInApi
    181 {
    182  public:
    183   MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit );
    184   ~MidiInAlsa( void );
    185   RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };
    186   void openPort( unsigned int portNumber, const std::string &portName );
    187   void openVirtualPort( const std::string &portName );
    188   void closePort( void );
    189   void setClientName( const std::string &clientName );
    190   void setPortName( const std::string &portName);
    191   unsigned int getPortCount( void );
    192   std::string getPortName( unsigned int portNumber );
    193 
    194  protected:
    195   void initialize( const std::string& clientName );
    196 };
    197 
    198 class MidiOutAlsa: public MidiOutApi
    199 {
    200  public:
    201   MidiOutAlsa( const std::string &clientName );
    202   ~MidiOutAlsa( void );
    203   RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };
    204   void openPort( unsigned int portNumber, const std::string &portName );
    205   void openVirtualPort( const std::string &portName );
    206   void closePort( void );
    207   void setClientName( const std::string &clientName );
    208   void setPortName( const std::string &portName );
    209   unsigned int getPortCount( void );
    210   std::string getPortName( unsigned int portNumber );
    211   void sendMessage( const unsigned char *message, size_t size );
    212 
    213  protected:
    214   void initialize( const std::string& clientName );
    215 };
    216 
    217 #endif
    218 
    219 #if defined(__WINDOWS_MM__)
    220 
    221 class MidiInWinMM: public MidiInApi
    222 {
    223  public:
    224   MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit );
    225   ~MidiInWinMM( void );
    226   RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };
    227   void openPort( unsigned int portNumber, const std::string &portName );
    228   void openVirtualPort( const std::string &portName );
    229   void closePort( void );
    230   void setClientName( const std::string &clientName );
    231   void setPortName( const std::string &portName );
    232   unsigned int getPortCount( void );
    233   std::string getPortName( unsigned int portNumber );
    234 
    235  protected:
    236   void initialize( const std::string& clientName );
    237 };
    238 
    239 class MidiOutWinMM: public MidiOutApi
    240 {
    241  public:
    242   MidiOutWinMM( const std::string &clientName );
    243   ~MidiOutWinMM( void );
    244   RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };
    245   void openPort( unsigned int portNumber, const std::string &portName );
    246   void openVirtualPort( const std::string &portName );
    247   void closePort( void );
    248   void setClientName( const std::string &clientName );
    249   void setPortName( const std::string &portName );
    250   unsigned int getPortCount( void );
    251   std::string getPortName( unsigned int portNumber );
    252   void sendMessage( const unsigned char *message, size_t size );
    253 
    254  protected:
    255   void initialize( const std::string& clientName );
    256 };
    257 
    258 #endif
    259 
    260 #if defined(__WEB_MIDI_API__)
    261 
    262 class MidiInWeb : public MidiInApi
    263 {
    264   std::string client_name{};
    265   std::string web_midi_id{};
    266   int open_port_number{-1};
    267 
    268  public:
    269   MidiInWeb(const std::string &/*clientName*/, unsigned int queueSizeLimit );
    270   ~MidiInWeb( void );
    271   RtMidi::Api getCurrentApi( void ) { return RtMidi::WEB_MIDI_API; };
    272   void openPort( unsigned int portNumber, const std::string &portName );
    273   void openVirtualPort( const std::string &portName );
    274   void closePort( void );
    275   void setClientName( const std::string &clientName );
    276   void setPortName( const std::string &portName );
    277   unsigned int getPortCount( void );
    278   std::string getPortName( unsigned int portNumber );
    279 
    280   void onMidiMessage( uint8_t* data, double domHishResTimeStamp );
    281 
    282  protected:
    283   void initialize( const std::string& clientName );
    284 };
    285 
    286 class MidiOutWeb: public MidiOutApi
    287 {
    288   std::string client_name{};
    289   std::string web_midi_id{};
    290   int open_port_number{-1};
    291 
    292  public:
    293   MidiOutWeb( const std::string &clientName );
    294   ~MidiOutWeb( void );
    295   RtMidi::Api getCurrentApi( void ) { return RtMidi::WEB_MIDI_API; };
    296   void openPort( unsigned int portNumber, const std::string &portName );
    297   void openVirtualPort( const std::string &portName );
    298   void closePort( void );
    299   void setClientName( const std::string &clientName );
    300   void setPortName( const std::string &portName );
    301   unsigned int getPortCount( void );
    302   std::string getPortName( unsigned int portNumber );
    303   void sendMessage( const unsigned char *message, size_t size );
    304 
    305  protected:
    306   void initialize( const std::string& clientName );
    307 };
    308 
    309 #endif
    310 
    311 #if defined(__RTMIDI_DUMMY__)
    312 
    313 class MidiInDummy: public MidiInApi
    314 {
    315  public:
    316  MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }
    317   RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }
    318   void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {}
    319   void openVirtualPort( const std::string &/*portName*/ ) {}
    320   void closePort( void ) {}
    321   void setClientName( const std::string &/*clientName*/ ) {};
    322   void setPortName( const std::string &/*portName*/ ) {};
    323   unsigned int getPortCount( void ) { return 0; }
    324   std::string getPortName( unsigned int /*portNumber*/ ) { return ""; }
    325 
    326  protected:
    327   void initialize( const std::string& /*clientName*/ ) {}
    328 };
    329 
    330 class MidiOutDummy: public MidiOutApi
    331 {
    332  public:
    333   MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }
    334   RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }
    335   void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {}
    336   void openVirtualPort( const std::string &/*portName*/ ) {}
    337   void closePort( void ) {}
    338   void setClientName( const std::string &/*clientName*/ ) {};
    339   void setPortName( const std::string &/*portName*/ ) {};
    340   unsigned int getPortCount( void ) { return 0; }
    341   std::string getPortName( unsigned int /*portNumber*/ ) { return ""; }
    342   void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {}
    343 
    344  protected:
    345   void initialize( const std::string& /*clientName*/ ) {}
    346 };
    347 
    348 #endif
    349 
    350 //*********************************************************************//
    351 //  RtMidi Definitions
    352 //*********************************************************************//
    353 
    354 RtMidi :: RtMidi()
    355   : rtapi_(0)
    356 {
    357 }
    358 
    359 RtMidi :: ~RtMidi()
    360 {
    361   delete rtapi_;
    362   rtapi_ = 0;
    363 }
    364 
    365 RtMidi::RtMidi(RtMidi&& other) noexcept {
    366     rtapi_ = other.rtapi_;
    367     other.rtapi_ = nullptr;
    368 }
    369 
    370 std::string RtMidi :: getVersion( void ) throw()
    371 {
    372   return std::string( RTMIDI_VERSION );
    373 }
    374 
    375 // Define API names and display names.
    376 // Must be in same order as API enum.
    377 extern "C" {
    378 const char* rtmidi_api_names[][2] = {
    379   { "unspecified" , "Unknown" },
    380   { "core"        , "CoreMidi" },
    381   { "alsa"        , "ALSA" },
    382   { "jack"        , "Jack" },
    383   { "winmm"       , "Windows MultiMedia" },
    384   { "web"         , "Web MIDI API" },
    385   { "dummy"       , "Dummy" },
    386 };
    387 const unsigned int rtmidi_num_api_names =
    388   sizeof(rtmidi_api_names)/sizeof(rtmidi_api_names[0]);
    389 
    390 // The order here will control the order of RtMidi's API search in
    391 // the constructor.
    392 extern "C" const RtMidi::Api rtmidi_compiled_apis[] = {
    393 #if defined(__MACOSX_CORE__)
    394   RtMidi::MACOSX_CORE,
    395 #endif
    396 #if defined(__LINUX_ALSA__)
    397   RtMidi::LINUX_ALSA,
    398 #endif
    399 #if defined(__UNIX_JACK__)
    400   RtMidi::UNIX_JACK,
    401 #endif
    402 #if defined(__WINDOWS_MM__)
    403   RtMidi::WINDOWS_MM,
    404 #endif
    405 #if defined(__WEB_MIDI_API__)
    406   RtMidi::WEB_MIDI_API,
    407 #endif
    408 #if defined(__RTMIDI_DUMMY__)
    409   RtMidi::RTMIDI_DUMMY,
    410 #endif
    411   RtMidi::UNSPECIFIED,
    412 };
    413 extern "C" const unsigned int rtmidi_num_compiled_apis =
    414   sizeof(rtmidi_compiled_apis)/sizeof(rtmidi_compiled_apis[0])-1;
    415 }
    416 
    417 void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()
    418 {
    419   apis = std::vector<RtMidi::Api>(rtmidi_compiled_apis,
    420                                   rtmidi_compiled_apis + rtmidi_num_compiled_apis);
    421 }
    422 
    423 std::string RtMidi :: getApiName( RtMidi::Api api )
    424 {
    425   if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS)
    426     return "";
    427   return rtmidi_api_names[api][0];
    428 }
    429 
    430 std::string RtMidi :: getApiDisplayName( RtMidi::Api api )
    431 {
    432   if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS)
    433     return "Unknown";
    434   return rtmidi_api_names[api][1];
    435 }
    436 
    437 RtMidi::Api RtMidi :: getCompiledApiByName( const std::string &name )
    438 {
    439   unsigned int i=0;
    440   for (i = 0; i < rtmidi_num_compiled_apis; ++i)
    441     if (name == rtmidi_api_names[rtmidi_compiled_apis[i]][0])
    442       return rtmidi_compiled_apis[i];
    443   return RtMidi::UNSPECIFIED;
    444 }
    445 
    446 void RtMidi :: setClientName( const std::string &clientName )
    447 {
    448   rtapi_->setClientName( clientName );
    449 }
    450 
    451 void RtMidi :: setPortName( const std::string &portName )
    452 {
    453   rtapi_->setPortName( portName );
    454 }
    455 
    456 
    457 //*********************************************************************//
    458 //  RtMidiIn Definitions
    459 //*********************************************************************//
    460 
    461 void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit )
    462 {
    463   delete rtapi_;
    464   rtapi_ = 0;
    465 
    466 #if defined(__UNIX_JACK__)
    467   if ( api == UNIX_JACK )
    468     rtapi_ = new MidiInJack( clientName, queueSizeLimit );
    469 #endif
    470 #if defined(__LINUX_ALSA__)
    471   if ( api == LINUX_ALSA )
    472     rtapi_ = new MidiInAlsa( clientName, queueSizeLimit );
    473 #endif
    474 #if defined(__WINDOWS_MM__)
    475   if ( api == WINDOWS_MM )
    476     rtapi_ = new MidiInWinMM( clientName, queueSizeLimit );
    477 #endif
    478 #if defined(__MACOSX_CORE__)
    479   if ( api == MACOSX_CORE )
    480     rtapi_ = new MidiInCore( clientName, queueSizeLimit );
    481 #endif
    482 #if defined(__WEB_MIDI_API__)
    483     if ( api == WEB_MIDI_API )
    484     rtapi_ = new MidiInWeb( clientName, queueSizeLimit );
    485 #endif
    486 #if defined(__RTMIDI_DUMMY__)
    487   if ( api == RTMIDI_DUMMY )
    488     rtapi_ = new MidiInDummy( clientName, queueSizeLimit );
    489 #endif
    490 }
    491 
    492 RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit )
    493   : RtMidi()
    494 {
    495   if ( api != UNSPECIFIED ) {
    496     // Attempt to open the specified API.
    497     openMidiApi( api, clientName, queueSizeLimit );
    498     if ( rtapi_ ) return;
    499 
    500     // No compiled support for specified API value.  Issue a warning
    501     // and continue as if no API was specified.
    502     std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl;
    503   }
    504 
    505   // Iterate through the compiled APIs and return as soon as we find
    506   // one with at least one port or we reach the end of the list.
    507   std::vector< RtMidi::Api > apis;
    508   getCompiledApi( apis );
    509   for ( unsigned int i=0; i<apis.size(); i++ ) {
    510     openMidiApi( apis[i], clientName, queueSizeLimit );
    511     if ( rtapi_ && rtapi_->getPortCount() ) break;
    512   }
    513 
    514   if ( rtapi_ ) return;
    515 
    516   // It should not be possible to get here because the preprocessor
    517   // definition __RTMIDI_DUMMY__ is automatically defined if no
    518   // API-specific definitions are passed to the compiler. But just in
    519   // case something weird happens, we'll throw an error.
    520   std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!";
    521   throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );
    522 }
    523 
    524 RtMidiIn :: ~RtMidiIn() throw()
    525 {
    526 }
    527 
    528 
    529 //*********************************************************************//
    530 //  RtMidiOut Definitions
    531 //*********************************************************************//
    532 
    533 void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName )
    534 {
    535   delete rtapi_;
    536   rtapi_ = 0;
    537 
    538 #if defined(__UNIX_JACK__)
    539   if ( api == UNIX_JACK )
    540     rtapi_ = new MidiOutJack( clientName );
    541 #endif
    542 #if defined(__LINUX_ALSA__)
    543   if ( api == LINUX_ALSA )
    544     rtapi_ = new MidiOutAlsa( clientName );
    545 #endif
    546 #if defined(__WINDOWS_MM__)
    547   if ( api == WINDOWS_MM )
    548     rtapi_ = new MidiOutWinMM( clientName );
    549 #endif
    550 #if defined(__MACOSX_CORE__)
    551   if ( api == MACOSX_CORE )
    552     rtapi_ = new MidiOutCore( clientName );
    553 #endif
    554 #if defined(__WEB_MIDI_API__)
    555     if ( api == WEB_MIDI_API )
    556     rtapi_ = new MidiOutWeb( clientName );
    557 #endif
    558 #if defined(__RTMIDI_DUMMY__)
    559   if ( api == RTMIDI_DUMMY )
    560     rtapi_ = new MidiOutDummy( clientName );
    561 #endif
    562 }
    563 
    564 RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName)
    565 {
    566   if ( api != UNSPECIFIED ) {
    567     // Attempt to open the specified API.
    568     openMidiApi( api, clientName );
    569     if ( rtapi_ ) return;
    570 
    571     // No compiled support for specified API value.  Issue a warning
    572     // and continue as if no API was specified.
    573     std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl;
    574   }
    575 
    576   // Iterate through the compiled APIs and return as soon as we find
    577   // one with at least one port or we reach the end of the list.
    578   std::vector< RtMidi::Api > apis;
    579   getCompiledApi( apis );
    580   for ( unsigned int i=0; i<apis.size(); i++ ) {
    581     openMidiApi( apis[i], clientName );
    582     if ( rtapi_ && rtapi_->getPortCount() ) break;
    583   }
    584 
    585   if ( rtapi_ ) return;
    586 
    587   // It should not be possible to get here because the preprocessor
    588   // definition __RTMIDI_DUMMY__ is automatically defined if no
    589   // API-specific definitions are passed to the compiler. But just in
    590   // case something weird happens, we'll thrown an error.
    591   std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!";
    592   throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );
    593 }
    594 
    595 RtMidiOut :: ~RtMidiOut() throw()
    596 {
    597 }
    598 
    599 //*********************************************************************//
    600 //  Common MidiApi Definitions
    601 //*********************************************************************//
    602 
    603 MidiApi :: MidiApi( void )
    604   : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0)
    605 {
    606 }
    607 
    608 MidiApi :: ~MidiApi( void )
    609 {
    610 }
    611 
    612 void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 )
    613 {
    614     errorCallback_ = errorCallback;
    615     errorCallbackUserData_ = userData;
    616 }
    617 
    618 void MidiApi :: error( RtMidiError::Type type, std::string errorString )
    619 {
    620   if ( errorCallback_ ) {
    621 
    622     if ( firstErrorOccurred_ )
    623       return;
    624 
    625     firstErrorOccurred_ = true;
    626     const std::string errorMessage = errorString;
    627 
    628     errorCallback_( type, errorMessage, errorCallbackUserData_ );
    629     firstErrorOccurred_ = false;
    630     return;
    631   }
    632 
    633   if ( type == RtMidiError::WARNING ) {
    634     std::cerr << '\n' << errorString << "\n\n";
    635   }
    636   else if ( type == RtMidiError::DEBUG_WARNING ) {
    637 #if defined(__RTMIDI_DEBUG__)
    638     std::cerr << '\n' << errorString << "\n\n";
    639 #endif
    640   }
    641   else {
    642     std::cerr << '\n' << errorString << "\n\n";
    643     throw RtMidiError( errorString, type );
    644   }
    645 }
    646 
    647 //*********************************************************************//
    648 //  Common MidiInApi Definitions
    649 //*********************************************************************//
    650 
    651 MidiInApi :: MidiInApi( unsigned int queueSizeLimit )
    652   : MidiApi()
    653 {
    654   // Allocate the MIDI queue.
    655   inputData_.queue.ringSize = queueSizeLimit;
    656   if ( inputData_.queue.ringSize > 0 )
    657     inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ];
    658 }
    659 
    660 MidiInApi :: ~MidiInApi( void )
    661 {
    662   // Delete the MIDI queue.
    663   if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring;
    664 }
    665 
    666 void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData )
    667 {
    668   if ( inputData_.usingCallback ) {
    669     errorString_ = "MidiInApi::setCallback: a callback function is already set!";
    670     error( RtMidiError::WARNING, errorString_ );
    671     return;
    672   }
    673 
    674   if ( !callback ) {
    675     errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";
    676     error( RtMidiError::WARNING, errorString_ );
    677     return;
    678   }
    679 
    680   inputData_.userCallback = callback;
    681   inputData_.userData = userData;
    682   inputData_.usingCallback = true;
    683 }
    684 
    685 void MidiInApi :: cancelCallback()
    686 {
    687   if ( !inputData_.usingCallback ) {
    688     errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";
    689     error( RtMidiError::WARNING, errorString_ );
    690     return;
    691   }
    692 
    693   inputData_.userCallback = 0;
    694   inputData_.userData = 0;
    695   inputData_.usingCallback = false;
    696 }
    697 
    698 void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )
    699 {
    700   inputData_.ignoreFlags = 0;
    701   if ( midiSysex ) inputData_.ignoreFlags = 0x01;
    702   if ( midiTime ) inputData_.ignoreFlags |= 0x02;
    703   if ( midiSense ) inputData_.ignoreFlags |= 0x04;
    704 }
    705 
    706 double MidiInApi :: getMessage( std::vector<unsigned char> *message )
    707 {
    708   message->clear();
    709 
    710   if ( inputData_.usingCallback ) {
    711     errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";
    712     error( RtMidiError::WARNING, errorString_ );
    713     return 0.0;
    714   }
    715 
    716   double timeStamp;
    717   if ( !inputData_.queue.pop( message, &timeStamp ) )
    718     return 0.0;
    719 
    720   return timeStamp;
    721 }
    722 
    723 void MidiInApi :: setBufferSize( unsigned int size, unsigned int count )
    724 {
    725     inputData_.bufferSize = size;
    726     inputData_.bufferCount = count;
    727 }
    728 
    729 unsigned int MidiInApi::MidiQueue::size( unsigned int *__back,
    730                                          unsigned int *__front )
    731 {
    732   // Access back/front members exactly once and make stack copies for
    733   // size calculation
    734   unsigned int _back = back, _front = front, _size;
    735   if ( _back >= _front )
    736     _size = _back - _front;
    737   else
    738     _size = ringSize - _front + _back;
    739 
    740   // Return copies of back/front so no new and unsynchronized accesses
    741   // to member variables are needed.
    742   if ( __back ) *__back = _back;
    743   if ( __front ) *__front = _front;
    744   return _size;
    745 }
    746 
    747 // As long as we haven't reached our queue size limit, push the message.
    748 bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg )
    749 {
    750   // Local stack copies of front/back
    751   unsigned int _back, _front, _size;
    752 
    753   // Get back/front indexes exactly once and calculate current size
    754   _size = size( &_back, &_front );
    755 
    756   if ( _size < ringSize-1 )
    757   {
    758     ring[_back] = msg;
    759     back = (back+1)%ringSize;
    760     return true;
    761   }
    762 
    763   return false;
    764 }
    765 
    766 bool MidiInApi::MidiQueue::pop( std::vector<unsigned char> *msg, double* timeStamp )
    767 {
    768   // Local stack copies of front/back
    769   unsigned int _back, _front, _size;
    770 
    771   // Get back/front indexes exactly once and calculate current size
    772   _size = size( &_back, &_front );
    773 
    774   if ( _size == 0 )
    775     return false;
    776 
    777   // Copy queued message to the vector pointer argument and then "pop" it.
    778   msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() );
    779   *timeStamp = ring[_front].timeStamp;
    780 
    781   // Update front
    782   front = (front+1)%ringSize;
    783   return true;
    784 }
    785 
    786 //*********************************************************************//
    787 //  Common MidiOutApi Definitions
    788 //*********************************************************************//
    789 
    790 MidiOutApi :: MidiOutApi( void )
    791   : MidiApi()
    792 {
    793 }
    794 
    795 MidiOutApi :: ~MidiOutApi( void )
    796 {
    797 }
    798 
    799 // *************************************************** //
    800 //
    801 // OS/API-specific methods.
    802 //
    803 // *************************************************** //
    804 
    805 #if defined(__MACOSX_CORE__)
    806 
    807 // The CoreMIDI API is based on the use of a callback function for
    808 // MIDI input.  We convert the system specific time stamps to delta
    809 // time values.
    810 
    811 // These are not available on iOS.
    812 #if (TARGET_OS_IPHONE == 0)
    813   #include <CoreAudio/HostTime.h>
    814   #include <CoreServices/CoreServices.h>
    815 #endif
    816 
    817 // A structure to hold variables related to the CoreMIDI API
    818 // implementation.
    819 struct CoreMidiData {
    820   MIDIClientRef client;
    821   MIDIPortRef port;
    822   MIDIEndpointRef endpoint;
    823   MIDIEndpointRef destinationId;
    824   unsigned long long lastTime;
    825   MIDISysexSendRequest sysexreq;
    826 };
    827 
    828 static MIDIClientRef CoreMidiClientSingleton = 0;
    829 
    830 void RtMidi_setCoreMidiClientSingleton(MIDIClientRef client){
    831   CoreMidiClientSingleton = client;
    832 }
    833 
    834 void RtMidi_disposeCoreMidiClientSingleton(){
    835   if (CoreMidiClientSingleton == 0){
    836     return;
    837   }
    838   MIDIClientDispose( CoreMidiClientSingleton );
    839   CoreMidiClientSingleton = 0;
    840 }
    841 
    842 //*********************************************************************//
    843 //  API: OS-X
    844 //  Class Definitions: MidiInCore
    845 //*********************************************************************//
    846 
    847 static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ )
    848 {
    849   MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef);
    850   CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
    851 
    852   unsigned char status;
    853   unsigned short nBytes, iByte, size;
    854   unsigned long long time;
    855 
    856   bool& continueSysex = data->continueSysex;
    857   MidiInApi::MidiMessage& message = data->message;
    858 
    859   const MIDIPacket *packet = &list->packet[0];
    860   for ( unsigned int i=0; i<list->numPackets; ++i ) {
    861 
    862     // My interpretation of the CoreMIDI documentation: all message
    863     // types, except sysex, are complete within a packet and there may
    864     // be several of them in a single packet.  Sysex messages can be
    865     // broken across multiple packets and PacketLists but are bundled
    866     // alone within each packet (these packets do not contain other
    867     // message types).  If sysex messages are split across multiple
    868     // MIDIPacketLists, they must be handled by multiple calls to this
    869     // function.
    870 
    871     nBytes = packet->length;
    872     if ( nBytes == 0 ) {
    873       packet = MIDIPacketNext( packet );
    874       continue;
    875     }
    876 
    877     // Calculate time stamp.
    878     if ( data->firstMessage ) {
    879       message.timeStamp = 0.0;
    880       data->firstMessage = false;
    881     }
    882     else {
    883       time = packet->timeStamp;
    884       if ( time == 0 ) { // this happens when receiving asynchronous sysex messages
    885         time = AudioGetCurrentHostTime();
    886       }
    887       time -= apiData->lastTime;
    888       time = AudioConvertHostTimeToNanos( time );
    889       if ( !continueSysex )
    890         message.timeStamp = time * 0.000000001;
    891     }
    892 
    893     // Track whether any non-filtered messages were found in this
    894     // packet for timestamp calculation
    895     bool foundNonFiltered = false;
    896 
    897     iByte = 0;
    898     if ( continueSysex ) {
    899       // We have a continuing, segmented sysex message.
    900       if ( !( data->ignoreFlags & 0x01 ) ) {
    901         // If we're not ignoring sysex messages, copy the entire packet.
    902         for ( unsigned int j=0; j<nBytes; ++j )
    903           message.bytes.push_back( packet->data[j] );
    904       }
    905       continueSysex = packet->data[nBytes-1] != 0xF7;
    906 
    907       if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) {
    908         // If not a continuing sysex message, invoke the user callback function or queue the message.
    909         if ( data->usingCallback ) {
    910           RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
    911           callback( message.timeStamp, &message.bytes, data->userData );
    912         }
    913         else {
    914           // As long as we haven't reached our queue size limit, push the message.
    915           if ( !data->queue.push( message ) )
    916             std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
    917         }
    918         message.bytes.clear();
    919       }
    920     }
    921     else {
    922       while ( iByte < nBytes ) {
    923         size = 0;
    924         // We are expecting that the next byte in the packet is a status byte.
    925         status = packet->data[iByte];
    926         if ( !(status & 0x80) ) break;
    927         // Determine the number of bytes in the MIDI message.
    928         if ( status < 0xC0 ) size = 3;
    929         else if ( status < 0xE0 ) size = 2;
    930         else if ( status < 0xF0 ) size = 3;
    931         else if ( status == 0xF0 ) {
    932           // A MIDI sysex
    933           if ( data->ignoreFlags & 0x01 ) {
    934             size = 0;
    935             iByte = nBytes;
    936           }
    937           else size = nBytes - iByte;
    938           continueSysex = packet->data[nBytes-1] != 0xF7;
    939         }
    940         else if ( status == 0xF1 ) {
    941           // A MIDI time code message
    942           if ( data->ignoreFlags & 0x02 ) {
    943             size = 0;
    944             iByte += 2;
    945           }
    946           else size = 2;
    947         }
    948         else if ( status == 0xF2 ) size = 3;
    949         else if ( status == 0xF3 ) size = 2;
    950         else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {
    951           // A MIDI timing tick message and we're ignoring it.
    952           size = 0;
    953           iByte += 1;
    954         }
    955         else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {
    956           // A MIDI active sensing message and we're ignoring it.
    957           size = 0;
    958           iByte += 1;
    959         }
    960         else size = 1;
    961 
    962         // Copy the MIDI data to our vector.
    963         if ( size ) {
    964           foundNonFiltered = true;
    965           message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );
    966           if ( !continueSysex ) {
    967             // If not a continuing sysex message, invoke the user callback function or queue the message.
    968             if ( data->usingCallback ) {
    969               RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
    970               callback( message.timeStamp, &message.bytes, data->userData );
    971             }
    972             else {
    973               // As long as we haven't reached our queue size limit, push the message.
    974               if ( !data->queue.push( message ) )
    975                 std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
    976             }
    977             message.bytes.clear();
    978           }
    979           iByte += size;
    980         }
    981       }
    982     }
    983 
    984     // Save the time of the last non-filtered message
    985     if ( foundNonFiltered ) {
    986       apiData->lastTime = packet->timeStamp;
    987       if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages
    988         apiData->lastTime = AudioGetCurrentHostTime();
    989       }
    990     }
    991 
    992     packet = MIDIPacketNext(packet);
    993   }
    994 }
    995 
    996 MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit )
    997   : MidiInApi( queueSizeLimit )
    998 {
    999   MidiInCore::initialize( clientName );
   1000 }
   1001 
   1002 MidiInCore :: ~MidiInCore( void )
   1003 {
   1004   // Close a connection if it exists.
   1005   MidiInCore::closePort();
   1006 
   1007   // Cleanup.
   1008   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1009   if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
   1010   delete data;
   1011 }
   1012 
   1013 MIDIClientRef MidiInCore::getCoreMidiClientSingleton(const std::string& clientName) throw() {
   1014 
   1015   if (CoreMidiClientSingleton == 0){
   1016       // Set up our client.
   1017       MIDIClientRef client;
   1018 
   1019       CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
   1020       OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
   1021       if ( result != noErr ) {
   1022         std::ostringstream ost;
   1023         ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
   1024         errorString_ = ost.str();
   1025         error( RtMidiError::DRIVER_ERROR, errorString_ );
   1026         return 0;
   1027       }
   1028       CFRelease( name );
   1029 
   1030       CoreMidiClientSingleton = client;
   1031   }
   1032 
   1033   return CoreMidiClientSingleton;
   1034 }
   1035 
   1036 void MidiInCore :: initialize( const std::string& clientName )
   1037 {
   1038   // Set up our client.
   1039   MIDIClientRef client = getCoreMidiClientSingleton(clientName);
   1040 
   1041   // Save our api-specific connection information.
   1042   CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
   1043   data->client = client;
   1044   data->endpoint = 0;
   1045   apiData_ = (void *) data;
   1046   inputData_.apiData = (void *) data;
   1047 }
   1048 
   1049 void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName )
   1050 {
   1051   if ( connected_ ) {
   1052     errorString_ = "MidiInCore::openPort: a valid connection already exists!";
   1053     error( RtMidiError::WARNING, errorString_ );
   1054     return;
   1055   }
   1056 
   1057   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
   1058   unsigned int nSrc = MIDIGetNumberOfSources();
   1059   if ( nSrc < 1 ) {
   1060     errorString_ = "MidiInCore::openPort: no MIDI input sources found!";
   1061     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
   1062     return;
   1063   }
   1064 
   1065   if ( portNumber >= nSrc ) {
   1066     std::ostringstream ost;
   1067     ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
   1068     errorString_ = ost.str();
   1069     error( RtMidiError::INVALID_PARAMETER, errorString_ );
   1070     return;
   1071   }
   1072 
   1073   MIDIPortRef port;
   1074   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1075   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
   1076   OSStatus result = MIDIInputPortCreate( data->client,
   1077                                          portNameRef,
   1078                                          midiInputCallback, (void *)&inputData_, &port );
   1079   CFRelease( portNameRef );
   1080 
   1081   if ( result != noErr ) {
   1082     errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
   1083     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1084     return;
   1085   }
   1086 
   1087   // Get the desired input source identifier.
   1088   MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
   1089   if ( endpoint == 0 ) {
   1090     MIDIPortDispose( port );
   1091     errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
   1092     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1093     return;
   1094   }
   1095 
   1096   // Make the connection.
   1097   result = MIDIPortConnectSource( port, endpoint, NULL );
   1098   if ( result != noErr ) {
   1099     MIDIPortDispose( port );
   1100     errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
   1101     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1102     return;
   1103   }
   1104 
   1105   // Save our api-specific port information.
   1106   data->port = port;
   1107 
   1108   connected_ = true;
   1109 }
   1110 
   1111 void MidiInCore :: openVirtualPort( const std::string &portName )
   1112 {
   1113   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1114 
   1115   // Create a virtual MIDI input destination.
   1116   MIDIEndpointRef endpoint;
   1117   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
   1118   OSStatus result = MIDIDestinationCreate( data->client,
   1119                                            portNameRef,
   1120                                            midiInputCallback, (void *)&inputData_, &endpoint );
   1121   CFRelease( portNameRef );
   1122 
   1123   if ( result != noErr ) {
   1124     errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination.";
   1125     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1126     return;
   1127   }
   1128 
   1129   // Save our api-specific connection information.
   1130   data->endpoint = endpoint;
   1131 }
   1132 
   1133 void MidiInCore :: closePort( void )
   1134 {
   1135   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1136 
   1137   if ( data->endpoint ) {
   1138     MIDIEndpointDispose( data->endpoint );
   1139     data->endpoint = 0;
   1140   }
   1141 
   1142   if ( data->port ) {
   1143     MIDIPortDispose( data->port );
   1144     data->port = 0;
   1145   }
   1146 
   1147   connected_ = false;
   1148 }
   1149 
   1150 void MidiInCore :: setClientName ( const std::string& )
   1151 {
   1152 
   1153   errorString_ = "MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API!";
   1154   error( RtMidiError::WARNING, errorString_ );
   1155 
   1156 }
   1157 
   1158 void MidiInCore :: setPortName ( const std::string& )
   1159 {
   1160 
   1161   errorString_ = "MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API!";
   1162   error( RtMidiError::WARNING, errorString_ );
   1163 
   1164 }
   1165 
   1166 unsigned int MidiInCore :: getPortCount()
   1167 {
   1168   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
   1169   return MIDIGetNumberOfSources();
   1170 }
   1171 
   1172 // This function was submitted by Douglas Casey Tucker and apparently
   1173 // derived largely from PortMidi.
   1174 CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
   1175 {
   1176   CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
   1177   CFStringRef str;
   1178 
   1179   // Begin with the endpoint's name.
   1180   str = NULL;
   1181   MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );
   1182   if ( str != NULL ) {
   1183     CFStringAppend( result, str );
   1184     CFRelease( str );
   1185   }
   1186 
   1187   // some MIDI devices have a leading space in endpoint name. trim
   1188   CFStringRef space = CFStringCreateWithCString(NULL, " ", kCFStringEncodingUTF8);
   1189   CFStringTrim(result, space);
   1190   CFRelease(space);
   1191 
   1192   MIDIEntityRef entity = 0;
   1193   MIDIEndpointGetEntity( endpoint, &entity );
   1194   if ( entity == 0 )
   1195     // probably virtual
   1196     return result;
   1197 
   1198   if ( CFStringGetLength( result ) == 0 ) {
   1199     // endpoint name has zero length -- try the entity
   1200     str = NULL;
   1201     MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );
   1202     if ( str != NULL ) {
   1203       CFStringAppend( result, str );
   1204       CFRelease( str );
   1205     }
   1206   }
   1207   // now consider the device's name
   1208   MIDIDeviceRef device = 0;
   1209   MIDIEntityGetDevice( entity, &device );
   1210   if ( device == 0 )
   1211     return result;
   1212 
   1213   str = NULL;
   1214   MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );
   1215   if ( CFStringGetLength( result ) == 0 ) {
   1216       CFRelease( result );
   1217       return str;
   1218   }
   1219   if ( str != NULL ) {
   1220     // if an external device has only one entity, throw away
   1221     // the endpoint name and just use the device name
   1222     if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {
   1223       CFRelease( result );
   1224       return str;
   1225     } else {
   1226       if ( CFStringGetLength( str ) == 0 ) {
   1227         CFRelease( str );
   1228         return result;
   1229       }
   1230       // does the entity name already start with the device name?
   1231       // (some drivers do this though they shouldn't)
   1232       // if so, do not prepend
   1233       if ( CFStringCompareWithOptions( result, /* endpoint name */
   1234                                        str /* device name */,
   1235                                        CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {
   1236         // prepend the device name to the entity name
   1237         if ( CFStringGetLength( result ) > 0 )
   1238           CFStringInsert( result, 0, CFSTR(" ") );
   1239 
   1240         CFStringInsert( result, 0, str );
   1241       }
   1242       CFRelease( str );
   1243     }
   1244   }
   1245   return result;
   1246 }
   1247 
   1248 // This function was submitted by Douglas Casey Tucker and apparently
   1249 // derived largely from PortMidi.
   1250 static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )
   1251 {
   1252   CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
   1253   CFStringRef str;
   1254   OSStatus err;
   1255   int i;
   1256 
   1257   // Does the endpoint have connections?
   1258   CFDataRef connections = NULL;
   1259   int nConnected = 0;
   1260   bool anyStrings = false;
   1261   err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );
   1262   if ( connections != NULL ) {
   1263     // It has connections, follow them
   1264     // Concatenate the names of all connected devices
   1265     nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);
   1266     if ( nConnected ) {
   1267       const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
   1268       for ( i=0; i<nConnected; ++i, ++pid ) {
   1269         MIDIUniqueID id = EndianS32_BtoN( *pid );
   1270         MIDIObjectRef connObject;
   1271         MIDIObjectType connObjectType;
   1272         err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );
   1273         if ( err == noErr ) {
   1274           if ( connObjectType == kMIDIObjectType_ExternalSource  ||
   1275                connObjectType == kMIDIObjectType_ExternalDestination ) {
   1276             // Connected to an external device's endpoint (10.3 and later).
   1277             str = EndpointName( (MIDIEndpointRef)(connObject), true );
   1278           } else {
   1279             // Connected to an external device (10.2) (or something else, catch-
   1280             str = NULL;
   1281             MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );
   1282           }
   1283           if ( str != NULL ) {
   1284             if ( anyStrings )
   1285               CFStringAppend( result, CFSTR(", ") );
   1286             else
   1287               anyStrings = true;
   1288             CFStringAppend( result, str );
   1289             CFRelease( str );
   1290           }
   1291         }
   1292       }
   1293     }
   1294     CFRelease( connections );
   1295   }
   1296   if ( anyStrings )
   1297     return result;
   1298 
   1299   CFRelease( result );
   1300 
   1301   // Here, either the endpoint had no connections, or we failed to obtain names
   1302   return EndpointName( endpoint, false );
   1303 }
   1304 
   1305 std::string MidiInCore :: getPortName( unsigned int portNumber )
   1306 {
   1307   CFStringRef nameRef;
   1308   MIDIEndpointRef portRef;
   1309   char name[128];
   1310 
   1311   std::string stringName;
   1312   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
   1313   if ( portNumber >= MIDIGetNumberOfSources() ) {
   1314     std::ostringstream ost;
   1315     ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
   1316     errorString_ = ost.str();
   1317     error( RtMidiError::WARNING, errorString_ );
   1318     return stringName;
   1319   }
   1320 
   1321   portRef = MIDIGetSource( portNumber );
   1322   nameRef = ConnectedEndpointName( portRef );
   1323   CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 );
   1324   CFRelease( nameRef );
   1325 
   1326   return stringName = name;
   1327 }
   1328 
   1329 //*********************************************************************//
   1330 //  API: OS-X
   1331 //  Class Definitions: MidiOutCore
   1332 //*********************************************************************//
   1333 
   1334 MidiOutCore :: MidiOutCore( const std::string &clientName )
   1335   : MidiOutApi()
   1336 {
   1337   MidiOutCore::initialize( clientName );
   1338 }
   1339 
   1340 MidiOutCore :: ~MidiOutCore( void )
   1341 {
   1342   // Close a connection if it exists.
   1343   MidiOutCore::closePort();
   1344 
   1345   // Cleanup.
   1346   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1347   if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
   1348   delete data;
   1349 }
   1350 
   1351 MIDIClientRef MidiOutCore::getCoreMidiClientSingleton(const std::string& clientName) throw() {
   1352 
   1353   if (CoreMidiClientSingleton == 0){
   1354       // Set up our client.
   1355       MIDIClientRef client;
   1356 
   1357       CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
   1358       OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
   1359       if ( result != noErr ) {
   1360         std::ostringstream ost;
   1361         ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
   1362         errorString_ = ost.str();
   1363         error( RtMidiError::DRIVER_ERROR, errorString_ );
   1364         return 0;
   1365       }
   1366       CFRelease( name );
   1367 
   1368       CoreMidiClientSingleton = client;
   1369   }
   1370 
   1371   return CoreMidiClientSingleton;
   1372 }
   1373 
   1374 void MidiOutCore :: initialize( const std::string& clientName )
   1375 {
   1376   // Set up our client.
   1377   MIDIClientRef client = getCoreMidiClientSingleton(clientName);
   1378 
   1379   // Save our api-specific connection information.
   1380   CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
   1381   data->client = client;
   1382   data->endpoint = 0;
   1383   apiData_ = (void *) data;
   1384 }
   1385 
   1386 unsigned int MidiOutCore :: getPortCount()
   1387 {
   1388   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
   1389   return MIDIGetNumberOfDestinations();
   1390 }
   1391 
   1392 std::string MidiOutCore :: getPortName( unsigned int portNumber )
   1393 {
   1394   CFStringRef nameRef;
   1395   MIDIEndpointRef portRef;
   1396   char name[128];
   1397 
   1398   std::string stringName;
   1399   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
   1400   if ( portNumber >= MIDIGetNumberOfDestinations() ) {
   1401     std::ostringstream ost;
   1402     ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
   1403     errorString_ = ost.str();
   1404     error( RtMidiError::WARNING, errorString_ );
   1405     return stringName;
   1406   }
   1407 
   1408   portRef = MIDIGetDestination( portNumber );
   1409   nameRef = ConnectedEndpointName(portRef);
   1410   CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 );
   1411   CFRelease( nameRef );
   1412 
   1413   return stringName = name;
   1414 }
   1415 
   1416 void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName )
   1417 {
   1418   if ( connected_ ) {
   1419     errorString_ = "MidiOutCore::openPort: a valid connection already exists!";
   1420     error( RtMidiError::WARNING, errorString_ );
   1421     return;
   1422   }
   1423 
   1424   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
   1425   unsigned int nDest = MIDIGetNumberOfDestinations();
   1426   if (nDest < 1) {
   1427     errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!";
   1428     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
   1429     return;
   1430   }
   1431 
   1432   if ( portNumber >= nDest ) {
   1433     std::ostringstream ost;
   1434     ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
   1435     errorString_ = ost.str();
   1436     error( RtMidiError::INVALID_PARAMETER, errorString_ );
   1437     return;
   1438   }
   1439 
   1440   MIDIPortRef port;
   1441   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1442   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
   1443   OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port );
   1444   CFRelease( portNameRef );
   1445   if ( result != noErr ) {
   1446     errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
   1447     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1448     return;
   1449   }
   1450 
   1451   // Get the desired output port identifier.
   1452   MIDIEndpointRef destination = MIDIGetDestination( portNumber );
   1453   if ( destination == 0 ) {
   1454     MIDIPortDispose( port );
   1455     errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
   1456     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1457     return;
   1458   }
   1459 
   1460   // Save our api-specific connection information.
   1461   data->port = port;
   1462   data->destinationId = destination;
   1463   connected_ = true;
   1464 }
   1465 
   1466 void MidiOutCore :: closePort( void )
   1467 {
   1468   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1469 
   1470   if ( data->endpoint ) {
   1471     MIDIEndpointDispose( data->endpoint );
   1472     data->endpoint = 0;
   1473   }
   1474 
   1475   if ( data->port ) {
   1476     MIDIPortDispose( data->port );
   1477     data->port = 0;
   1478   }
   1479 
   1480   connected_ = false;
   1481 }
   1482 
   1483 void MidiOutCore :: setClientName ( const std::string& )
   1484 {
   1485 
   1486   errorString_ = "MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API!";
   1487   error( RtMidiError::WARNING, errorString_ );
   1488 
   1489 }
   1490 
   1491 void MidiOutCore :: setPortName ( const std::string& )
   1492 {
   1493 
   1494   errorString_ = "MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API!";
   1495   error( RtMidiError::WARNING, errorString_ );
   1496 
   1497 }
   1498 
   1499 void MidiOutCore :: openVirtualPort( const std::string &portName )
   1500 {
   1501   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1502 
   1503   if ( data->endpoint ) {
   1504     errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!";
   1505     error( RtMidiError::WARNING, errorString_ );
   1506     return;
   1507   }
   1508 
   1509   // Create a virtual MIDI output source.
   1510   MIDIEndpointRef endpoint;
   1511   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
   1512   OSStatus result = MIDISourceCreate( data->client, portNameRef, &endpoint );
   1513   CFRelease( portNameRef );
   1514 
   1515   if ( result != noErr ) {
   1516     errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source.";
   1517     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1518     return;
   1519   }
   1520 
   1521   // Save our api-specific connection information.
   1522   data->endpoint = endpoint;
   1523 }
   1524 
   1525 void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )
   1526 {
   1527   // We use the MIDISendSysex() function to asynchronously send sysex
   1528   // messages.  Otherwise, we use a single CoreMidi MIDIPacket.
   1529   unsigned int nBytes = static_cast<unsigned int> (size);
   1530   if ( nBytes == 0 ) {
   1531     errorString_ = "MidiOutCore::sendMessage: no data in message argument!";
   1532     error( RtMidiError::WARNING, errorString_ );
   1533     return;
   1534   }
   1535 
   1536   if ( message[0] != 0xF0 && nBytes > 3 ) {
   1537     errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
   1538     error( RtMidiError::WARNING, errorString_ );
   1539     return;
   1540   }
   1541 
   1542   MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
   1543   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
   1544   OSStatus result;
   1545 
   1546   ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes;
   1547   Byte buffer[bufsize+16]; // pad for other struct members
   1548   ByteCount listSize = sizeof( buffer );
   1549   MIDIPacketList *packetList = (MIDIPacketList*)buffer;
   1550 
   1551   ByteCount remainingBytes = nBytes;
   1552   while ( remainingBytes ) {
   1553     MIDIPacket *packet = MIDIPacketListInit( packetList );
   1554     // A MIDIPacketList can only contain a maximum of 64K of data, so if our message is longer,
   1555     // break it up into chunks of 64K or less and send out as a MIDIPacketList with only one
   1556     // MIDIPacket. Here, we reuse the memory allocated above on the stack for all.
   1557     ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes;
   1558     const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes];
   1559     packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr );
   1560     remainingBytes -= bytesForPacket;
   1561 
   1562     if ( !packet ) {
   1563       errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
   1564       error( RtMidiError::DRIVER_ERROR, errorString_ );
   1565       return;
   1566     }
   1567 
   1568     // Send to any destinations that may have connected to us.
   1569     if ( data->endpoint ) {
   1570       result = MIDIReceived( data->endpoint, packetList );
   1571       if ( result != noErr ) {
   1572         errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
   1573         error( RtMidiError::WARNING, errorString_ );
   1574       }
   1575     }
   1576 
   1577     // And send to an explicit destination port if we're connected.
   1578     if ( connected_ ) {
   1579       result = MIDISend( data->port, data->destinationId, packetList );
   1580       if ( result != noErr ) {
   1581         errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
   1582         error( RtMidiError::WARNING, errorString_ );
   1583       }
   1584     }
   1585   }
   1586 }
   1587 
   1588 #endif  // __MACOSX_CORE__
   1589 
   1590 
   1591 //*********************************************************************//
   1592 //  API: LINUX ALSA SEQUENCER
   1593 //*********************************************************************//
   1594 
   1595 // API information found at:
   1596 //   - http://www.alsa-project.org/documentation.php#Library
   1597 
   1598 #if defined(__LINUX_ALSA__)
   1599 
   1600 // The ALSA Sequencer API is based on the use of a callback function for
   1601 // MIDI input.
   1602 //
   1603 // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
   1604 // time stamps and other assorted fixes!!!
   1605 
   1606 // If you don't need timestamping for incoming MIDI events, define the
   1607 // preprocessor definition AVOID_TIMESTAMPING to save resources
   1608 // associated with the ALSA sequencer queues.
   1609 
   1610 #include <pthread.h>
   1611 #include <sys/time.h>
   1612 
   1613 // ALSA header file.
   1614 #include <alsa/asoundlib.h>
   1615 
   1616 // A structure to hold variables related to the ALSA API
   1617 // implementation.
   1618 struct AlsaMidiData {
   1619   snd_seq_t *seq;
   1620   unsigned int portNum;
   1621   int vport;
   1622   snd_seq_port_subscribe_t *subscription;
   1623   snd_midi_event_t *coder;
   1624   unsigned int bufferSize;
   1625   unsigned int requestedBufferSize;
   1626   unsigned char *buffer;
   1627   pthread_t thread;
   1628   pthread_t dummy_thread_id;
   1629   snd_seq_real_time_t lastTime;
   1630   int queue_id; // an input queue is needed to get timestamped events
   1631   int trigger_fds[2];
   1632 };
   1633 
   1634 #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
   1635 
   1636 //*********************************************************************//
   1637 //  API: LINUX ALSA
   1638 //  Class Definitions: MidiInAlsa
   1639 //*********************************************************************//
   1640 
   1641 static void *alsaMidiHandler( void *ptr )
   1642 {
   1643   MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr);
   1644   AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);
   1645 
   1646   long nBytes;
   1647   double time;
   1648   bool continueSysex = false;
   1649   bool doDecode = false;
   1650   MidiInApi::MidiMessage message;
   1651   int poll_fd_count;
   1652   struct pollfd *poll_fds;
   1653 
   1654   snd_seq_event_t *ev;
   1655   int result;
   1656   result = snd_midi_event_new( 0, &apiData->coder );
   1657   if ( result < 0 ) {
   1658     data->doInput = false;
   1659     std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n";
   1660     return 0;
   1661   }
   1662   unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );
   1663   if ( buffer == NULL ) {
   1664     data->doInput = false;
   1665     snd_midi_event_free( apiData->coder );
   1666     apiData->coder = 0;
   1667     std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n";
   1668     return 0;
   1669   }
   1670   snd_midi_event_init( apiData->coder );
   1671   snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages
   1672 
   1673   poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1;
   1674   poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd ));
   1675   snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN );
   1676   poll_fds[0].fd = apiData->trigger_fds[0];
   1677   poll_fds[0].events = POLLIN;
   1678 
   1679   while ( data->doInput ) {
   1680 
   1681     if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {
   1682       // No data pending
   1683       if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) {
   1684         if ( poll_fds[0].revents & POLLIN ) {
   1685           bool dummy;
   1686           int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) );
   1687           (void) res;
   1688         }
   1689       }
   1690       continue;
   1691     }
   1692 
   1693     // If here, there should be data.
   1694     result = snd_seq_event_input( apiData->seq, &ev );
   1695     if ( result == -ENOSPC ) {
   1696       std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n";
   1697       continue;
   1698     }
   1699     else if ( result <= 0 ) {
   1700       std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n";
   1701       perror("System reports");
   1702       continue;
   1703     }
   1704 
   1705     // This is a bit weird, but we now have to decode an ALSA MIDI
   1706     // event (back) into MIDI bytes.  We'll ignore non-MIDI types.
   1707     if ( !continueSysex ) message.bytes.clear();
   1708 
   1709     doDecode = false;
   1710     switch ( ev->type ) {
   1711 
   1712     case SND_SEQ_EVENT_PORT_SUBSCRIBED:
   1713 #if defined(__RTMIDI_DEBUG__)
   1714       std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n";
   1715 #endif
   1716       break;
   1717 
   1718     case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
   1719 #if defined(__RTMIDI_DEBUG__)
   1720       std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n";
   1721       std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"
   1722                 << (int) ev->data.connect.sender.port
   1723                 << ", dest = " << (int) ev->data.connect.dest.client << ":"
   1724                 << (int) ev->data.connect.dest.port
   1725                 << std::endl;
   1726 #endif
   1727       break;
   1728 
   1729     case SND_SEQ_EVENT_QFRAME: // MIDI time code
   1730       if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
   1731       break;
   1732 
   1733     case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick
   1734       if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
   1735       break;
   1736 
   1737     case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick
   1738       if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
   1739       break;
   1740 
   1741     case SND_SEQ_EVENT_SENSING: // Active sensing
   1742       if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;
   1743       break;
   1744 
   1745     case SND_SEQ_EVENT_SYSEX:
   1746       if ( (data->ignoreFlags & 0x01) ) break;
   1747       if ( ev->data.ext.len > apiData->bufferSize ) {
   1748         apiData->bufferSize = ev->data.ext.len;
   1749         free( buffer );
   1750         buffer = (unsigned char *) malloc( apiData->bufferSize );
   1751         if ( buffer == NULL ) {
   1752           data->doInput = false;
   1753           std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n";
   1754           break;
   1755         }
   1756       }
   1757       doDecode = true;
   1758       break;
   1759 
   1760     default:
   1761       doDecode = true;
   1762     }
   1763 
   1764     if ( doDecode ) {
   1765 
   1766       nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
   1767       if ( nBytes > 0 ) {
   1768         // The ALSA sequencer has a maximum buffer size for MIDI sysex
   1769         // events of 256 bytes.  If a device sends sysex messages larger
   1770         // than this, they are segmented into 256 byte chunks.  So,
   1771         // we'll watch for this and concatenate sysex chunks into a
   1772         // single sysex message if necessary.
   1773         if ( !continueSysex )
   1774           message.bytes.assign( buffer, &buffer[nBytes] );
   1775         else
   1776           message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
   1777 
   1778         continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
   1779         if ( !continueSysex ) {
   1780 
   1781           // Calculate the time stamp:
   1782           message.timeStamp = 0.0;
   1783 
   1784           // Method 1: Use the system time.
   1785           //(void)gettimeofday(&tv, (struct timezone *)NULL);
   1786           //time = (tv.tv_sec * 1000000) + tv.tv_usec;
   1787 
   1788           // Method 2: Use the ALSA sequencer event time data.
   1789           // (thanks to Pedro Lopez-Cabanillas!).
   1790 
   1791           // Using method from:
   1792           // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
   1793 
   1794           // Perform the carry for the later subtraction by updating y.
   1795           // Temp var y is timespec because computation requires signed types,
   1796           // while snd_seq_real_time_t has unsigned types.
   1797           snd_seq_real_time_t &x( ev->time.time );
   1798           struct timespec y;
   1799           y.tv_nsec = apiData->lastTime.tv_nsec;
   1800           y.tv_sec = apiData->lastTime.tv_sec;
   1801           if ( x.tv_nsec < y.tv_nsec ) {
   1802               int nsec = (y.tv_nsec - (int)x.tv_nsec) / 1000000000 + 1;
   1803               y.tv_nsec -= 1000000000 * nsec;
   1804               y.tv_sec += nsec;
   1805           }
   1806           if ( x.tv_nsec - y.tv_nsec > 1000000000 ) {
   1807               int nsec = ((int)x.tv_nsec - y.tv_nsec) / 1000000000;
   1808               y.tv_nsec += 1000000000 * nsec;
   1809               y.tv_sec -= nsec;
   1810           }
   1811 
   1812           // Compute the time difference.
   1813           time = (int)x.tv_sec - y.tv_sec + ((int)x.tv_nsec - y.tv_nsec)*1e-9;
   1814 
   1815           apiData->lastTime = ev->time.time;
   1816 
   1817           if ( data->firstMessage == true )
   1818             data->firstMessage = false;
   1819           else
   1820             message.timeStamp = time;
   1821         }
   1822         else {
   1823 #if defined(__RTMIDI_DEBUG__)
   1824           std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
   1825 #endif
   1826         }
   1827       }
   1828     }
   1829 
   1830     snd_seq_free_event( ev );
   1831     if ( message.bytes.size() == 0 || continueSysex ) continue;
   1832 
   1833     if ( data->usingCallback ) {
   1834       RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
   1835       callback( message.timeStamp, &message.bytes, data->userData );
   1836     }
   1837     else {
   1838       // As long as we haven't reached our queue size limit, push the message.
   1839       if ( !data->queue.push( message ) )
   1840         std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n";
   1841     }
   1842   }
   1843 
   1844   if ( buffer ) free( buffer );
   1845   snd_midi_event_free( apiData->coder );
   1846   apiData->coder = 0;
   1847   apiData->thread = apiData->dummy_thread_id;
   1848   return 0;
   1849 }
   1850 
   1851 MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit )
   1852   : MidiInApi( queueSizeLimit )
   1853 {
   1854   MidiInAlsa::initialize( clientName );
   1855 }
   1856 
   1857 MidiInAlsa :: ~MidiInAlsa()
   1858 {
   1859   // Close a connection if it exists.
   1860   MidiInAlsa::closePort();
   1861 
   1862   // Shutdown the input thread.
   1863   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   1864   if ( inputData_.doInput ) {
   1865     inputData_.doInput = false;
   1866     int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) );
   1867     (void) res;
   1868     if ( !pthread_equal(data->thread, data->dummy_thread_id) )
   1869       pthread_join( data->thread, NULL );
   1870   }
   1871 
   1872   // Cleanup.
   1873   close ( data->trigger_fds[0] );
   1874   close ( data->trigger_fds[1] );
   1875   if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
   1876 #ifndef AVOID_TIMESTAMPING
   1877   snd_seq_free_queue( data->seq, data->queue_id );
   1878 #endif
   1879   snd_seq_close( data->seq );
   1880   delete data;
   1881 }
   1882 
   1883 void MidiInAlsa :: initialize( const std::string& clientName )
   1884 {
   1885   // Set up the ALSA sequencer client.
   1886   snd_seq_t *seq;
   1887   int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK );
   1888   if ( result < 0 ) {
   1889     errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object.";
   1890     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1891     return;
   1892   }
   1893 
   1894   // Set client name.
   1895   snd_seq_set_client_name( seq, clientName.c_str() );
   1896 
   1897   // Save our api-specific connection information.
   1898   AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
   1899   data->seq = seq;
   1900   data->portNum = -1;
   1901   data->vport = -1;
   1902   data->subscription = 0;
   1903   data->dummy_thread_id = pthread_self();
   1904   data->thread = data->dummy_thread_id;
   1905   data->trigger_fds[0] = -1;
   1906   data->trigger_fds[1] = -1;
   1907   data->bufferSize = inputData_.bufferSize;
   1908   apiData_ = (void *) data;
   1909   inputData_.apiData = (void *) data;
   1910 
   1911   if ( pipe(data->trigger_fds) == -1 ) {
   1912     errorString_ = "MidiInAlsa::initialize: error creating pipe objects.";
   1913     error( RtMidiError::DRIVER_ERROR, errorString_ );
   1914     return;
   1915   }
   1916 
   1917   // Create the input queue
   1918 #ifndef AVOID_TIMESTAMPING
   1919   data->queue_id = snd_seq_alloc_named_queue( seq, "RtMidi Queue" );
   1920   // Set arbitrary tempo (mm=100) and resolution (240)
   1921   snd_seq_queue_tempo_t *qtempo;
   1922   snd_seq_queue_tempo_alloca( &qtempo );
   1923   snd_seq_queue_tempo_set_tempo( qtempo, 600000 );
   1924   snd_seq_queue_tempo_set_ppq( qtempo, 240 );
   1925   snd_seq_set_queue_tempo( data->seq, data->queue_id, qtempo );
   1926   snd_seq_drain_output( data->seq );
   1927 #endif
   1928 }
   1929 
   1930 // This function is used to count or get the pinfo structure for a given port number.
   1931 unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )
   1932 {
   1933   snd_seq_client_info_t *cinfo;
   1934   int client;
   1935   int count = 0;
   1936   snd_seq_client_info_alloca( &cinfo );
   1937 
   1938   snd_seq_client_info_set_client( cinfo, -1 );
   1939   while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {
   1940     client = snd_seq_client_info_get_client( cinfo );
   1941     if ( client == 0 ) continue;
   1942     // Reset query info
   1943     snd_seq_port_info_set_client( pinfo, client );
   1944     snd_seq_port_info_set_port( pinfo, -1 );
   1945     while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
   1946       unsigned int atyp = snd_seq_port_info_get_type( pinfo );
   1947       if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&
   1948            ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) &&
   1949            ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue;
   1950 
   1951       unsigned int caps = snd_seq_port_info_get_capability( pinfo );
   1952       if ( ( caps & type ) != type ) continue;
   1953       if ( count == portNumber ) return 1;
   1954       ++count;
   1955     }
   1956   }
   1957 
   1958   // If a negative portNumber was used, return the port count.
   1959   if ( portNumber < 0 ) return count;
   1960   return 0;
   1961 }
   1962 
   1963 unsigned int MidiInAlsa :: getPortCount()
   1964 {
   1965   snd_seq_port_info_t *pinfo;
   1966   snd_seq_port_info_alloca( &pinfo );
   1967 
   1968   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   1969   return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );
   1970 }
   1971 
   1972 std::string MidiInAlsa :: getPortName( unsigned int portNumber )
   1973 {
   1974   snd_seq_client_info_t *cinfo;
   1975   snd_seq_port_info_t *pinfo;
   1976   snd_seq_client_info_alloca( &cinfo );
   1977   snd_seq_port_info_alloca( &pinfo );
   1978 
   1979   std::string stringName;
   1980   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   1981   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
   1982     int cnum = snd_seq_port_info_get_client( pinfo );
   1983     snd_seq_get_any_client_info( data->seq, cnum, cinfo );
   1984     std::ostringstream os;
   1985     os << snd_seq_client_info_get_name( cinfo );
   1986     os << ":";
   1987     os << snd_seq_port_info_get_name( pinfo );
   1988     os << " ";                                    // These lines added to make sure devices are listed
   1989     os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names
   1990     os << ":";
   1991     os << snd_seq_port_info_get_port( pinfo );
   1992     stringName = os.str();
   1993     return stringName;
   1994   }
   1995 
   1996   // If we get here, we didn't find a match.
   1997   errorString_ = "MidiInAlsa::getPortName: error looking for port name!";
   1998   error( RtMidiError::WARNING, errorString_ );
   1999   return stringName;
   2000 }
   2001 
   2002 void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName )
   2003 {
   2004   if ( connected_ ) {
   2005     errorString_ = "MidiInAlsa::openPort: a valid connection already exists!";
   2006     error( RtMidiError::WARNING, errorString_ );
   2007     return;
   2008   }
   2009 
   2010   unsigned int nSrc = this->getPortCount();
   2011   if ( nSrc < 1 ) {
   2012     errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!";
   2013     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
   2014     return;
   2015   }
   2016 
   2017   snd_seq_port_info_t *src_pinfo;
   2018   snd_seq_port_info_alloca( &src_pinfo );
   2019   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2020   if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {
   2021     std::ostringstream ost;
   2022     ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
   2023     errorString_ = ost.str();
   2024     error( RtMidiError::INVALID_PARAMETER, errorString_ );
   2025     return;
   2026   }
   2027 
   2028   snd_seq_addr_t sender, receiver;
   2029   sender.client = snd_seq_port_info_get_client( src_pinfo );
   2030   sender.port = snd_seq_port_info_get_port( src_pinfo );
   2031   receiver.client = snd_seq_client_id( data->seq );
   2032 
   2033   snd_seq_port_info_t *pinfo;
   2034   snd_seq_port_info_alloca( &pinfo );
   2035   if ( data->vport < 0 ) {
   2036     snd_seq_port_info_set_client( pinfo, 0 );
   2037     snd_seq_port_info_set_port( pinfo, 0 );
   2038     snd_seq_port_info_set_capability( pinfo,
   2039                                       SND_SEQ_PORT_CAP_WRITE |
   2040                                       SND_SEQ_PORT_CAP_SUBS_WRITE );
   2041     snd_seq_port_info_set_type( pinfo,
   2042                                 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
   2043                                 SND_SEQ_PORT_TYPE_APPLICATION );
   2044     snd_seq_port_info_set_midi_channels(pinfo, 16);
   2045 #ifndef AVOID_TIMESTAMPING
   2046     snd_seq_port_info_set_timestamping( pinfo, 1 );
   2047     snd_seq_port_info_set_timestamp_real( pinfo, 1 );
   2048     snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id );
   2049 #endif
   2050     snd_seq_port_info_set_name( pinfo,  portName.c_str() );
   2051     data->vport = snd_seq_create_port( data->seq, pinfo );
   2052 
   2053     if ( data->vport < 0 ) {
   2054       errorString_ = "MidiInAlsa::openPort: ALSA error creating input port.";
   2055       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2056       return;
   2057     }
   2058     data->vport = snd_seq_port_info_get_port( pinfo );
   2059   }
   2060 
   2061   receiver.port = data->vport;
   2062 
   2063   if ( !data->subscription ) {
   2064     // Make subscription
   2065     if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) {
   2066       errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription.";
   2067       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2068       return;
   2069     }
   2070     snd_seq_port_subscribe_set_sender( data->subscription, &sender );
   2071     snd_seq_port_subscribe_set_dest( data->subscription, &receiver );
   2072     if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) {
   2073       snd_seq_port_subscribe_free( data->subscription );
   2074       data->subscription = 0;
   2075       errorString_ = "MidiInAlsa::openPort: ALSA error making port connection.";
   2076       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2077       return;
   2078     }
   2079   }
   2080 
   2081   if ( inputData_.doInput == false ) {
   2082     // Start the input queue
   2083 #ifndef AVOID_TIMESTAMPING
   2084     snd_seq_start_queue( data->seq, data->queue_id, NULL );
   2085     snd_seq_drain_output( data->seq );
   2086 #endif
   2087     // Start our MIDI input thread.
   2088     pthread_attr_t attr;
   2089     pthread_attr_init( &attr );
   2090     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
   2091     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
   2092 
   2093     inputData_.doInput = true;
   2094     int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ );
   2095     pthread_attr_destroy( &attr );
   2096     if ( err ) {
   2097       snd_seq_unsubscribe_port( data->seq, data->subscription );
   2098       snd_seq_port_subscribe_free( data->subscription );
   2099       data->subscription = 0;
   2100       inputData_.doInput = false;
   2101       errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
   2102       error( RtMidiError::THREAD_ERROR, errorString_ );
   2103       return;
   2104     }
   2105   }
   2106 
   2107   connected_ = true;
   2108 }
   2109 
   2110 void MidiInAlsa :: openVirtualPort( const std::string &portName )
   2111 {
   2112   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2113   if ( data->vport < 0 ) {
   2114     snd_seq_port_info_t *pinfo;
   2115     snd_seq_port_info_alloca( &pinfo );
   2116     snd_seq_port_info_set_capability( pinfo,
   2117                                       SND_SEQ_PORT_CAP_WRITE |
   2118                                       SND_SEQ_PORT_CAP_SUBS_WRITE );
   2119     snd_seq_port_info_set_type( pinfo,
   2120                                 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
   2121                                 SND_SEQ_PORT_TYPE_APPLICATION );
   2122     snd_seq_port_info_set_midi_channels( pinfo, 16 );
   2123 #ifndef AVOID_TIMESTAMPING
   2124     snd_seq_port_info_set_timestamping( pinfo, 1 );
   2125     snd_seq_port_info_set_timestamp_real( pinfo, 1 );
   2126     snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id );
   2127 #endif
   2128     snd_seq_port_info_set_name( pinfo, portName.c_str() );
   2129     data->vport = snd_seq_create_port( data->seq, pinfo );
   2130 
   2131     if ( data->vport < 0 ) {
   2132       errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port.";
   2133       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2134       return;
   2135     }
   2136     data->vport = snd_seq_port_info_get_port( pinfo );
   2137   }
   2138 
   2139   if ( inputData_.doInput == false ) {
   2140     // Wait for old thread to stop, if still running
   2141     if ( !pthread_equal( data->thread, data->dummy_thread_id ) )
   2142       pthread_join( data->thread, NULL );
   2143 
   2144     // Start the input queue
   2145 #ifndef AVOID_TIMESTAMPING
   2146     snd_seq_start_queue( data->seq, data->queue_id, NULL );
   2147     snd_seq_drain_output( data->seq );
   2148 #endif
   2149     // Start our MIDI input thread.
   2150     pthread_attr_t attr;
   2151     pthread_attr_init( &attr );
   2152     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
   2153     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
   2154 
   2155     inputData_.doInput = true;
   2156     int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ );
   2157     pthread_attr_destroy( &attr );
   2158     if ( err ) {
   2159       if ( data->subscription ) {
   2160         snd_seq_unsubscribe_port( data->seq, data->subscription );
   2161         snd_seq_port_subscribe_free( data->subscription );
   2162         data->subscription = 0;
   2163       }
   2164       inputData_.doInput = false;
   2165       errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
   2166       error( RtMidiError::THREAD_ERROR, errorString_ );
   2167       return;
   2168     }
   2169   }
   2170 }
   2171 
   2172 void MidiInAlsa :: closePort( void )
   2173 {
   2174   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2175 
   2176   if ( connected_ ) {
   2177     if ( data->subscription ) {
   2178       snd_seq_unsubscribe_port( data->seq, data->subscription );
   2179       snd_seq_port_subscribe_free( data->subscription );
   2180       data->subscription = 0;
   2181     }
   2182     // Stop the input queue
   2183 #ifndef AVOID_TIMESTAMPING
   2184     snd_seq_stop_queue( data->seq, data->queue_id, NULL );
   2185     snd_seq_drain_output( data->seq );
   2186 #endif
   2187     connected_ = false;
   2188   }
   2189 
   2190   // Stop thread to avoid triggering the callback, while the port is intended to be closed
   2191   if ( inputData_.doInput ) {
   2192     inputData_.doInput = false;
   2193     int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) );
   2194     (void) res;
   2195     if ( !pthread_equal( data->thread, data->dummy_thread_id ) )
   2196       pthread_join( data->thread, NULL );
   2197   }
   2198 }
   2199 
   2200 void MidiInAlsa :: setClientName( const std::string &clientName )
   2201 {
   2202 
   2203   AlsaMidiData *data = static_cast<AlsaMidiData *> ( apiData_ );
   2204   snd_seq_set_client_name( data->seq, clientName.c_str() );
   2205 
   2206 }
   2207 
   2208 void MidiInAlsa :: setPortName( const std::string &portName )
   2209 {
   2210   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2211   snd_seq_port_info_t *pinfo;
   2212   snd_seq_port_info_alloca( &pinfo );
   2213   snd_seq_get_port_info( data->seq, data->vport, pinfo );
   2214   snd_seq_port_info_set_name( pinfo, portName.c_str() );
   2215   snd_seq_set_port_info( data->seq, data->vport, pinfo );
   2216 }
   2217 
   2218 //*********************************************************************//
   2219 //  API: LINUX ALSA
   2220 //  Class Definitions: MidiOutAlsa
   2221 //*********************************************************************//
   2222 
   2223 MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi()
   2224 {
   2225   MidiOutAlsa::initialize( clientName );
   2226 }
   2227 
   2228 MidiOutAlsa :: ~MidiOutAlsa()
   2229 {
   2230   // Close a connection if it exists.
   2231   MidiOutAlsa::closePort();
   2232 
   2233   // Cleanup.
   2234   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2235   if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
   2236   if ( data->coder ) snd_midi_event_free( data->coder );
   2237   if ( data->buffer ) free( data->buffer );
   2238   snd_seq_close( data->seq );
   2239   delete data;
   2240 }
   2241 
   2242 void MidiOutAlsa :: initialize( const std::string& clientName )
   2243 {
   2244   // Set up the ALSA sequencer client.
   2245   snd_seq_t *seq;
   2246   int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
   2247   if ( result1 < 0 ) {
   2248     errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object.";
   2249     error( RtMidiError::DRIVER_ERROR, errorString_ );
   2250     return;
   2251   }
   2252 
   2253   // Set client name.
   2254   snd_seq_set_client_name( seq, clientName.c_str() );
   2255 
   2256   // Save our api-specific connection information.
   2257   AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
   2258   data->seq = seq;
   2259   data->portNum = -1;
   2260   data->vport = -1;
   2261   data->bufferSize = 32;
   2262   data->coder = 0;
   2263   data->buffer = 0;
   2264   int result = snd_midi_event_new( data->bufferSize, &data->coder );
   2265   if ( result < 0 ) {
   2266     delete data;
   2267     errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n";
   2268     error( RtMidiError::DRIVER_ERROR, errorString_ );
   2269     return;
   2270   }
   2271   data->buffer = (unsigned char *) malloc( data->bufferSize );
   2272   if ( data->buffer == NULL ) {
   2273     delete data;
   2274     errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
   2275     error( RtMidiError::MEMORY_ERROR, errorString_ );
   2276     return;
   2277   }
   2278   snd_midi_event_init( data->coder );
   2279   apiData_ = (void *) data;
   2280 }
   2281 
   2282 unsigned int MidiOutAlsa :: getPortCount()
   2283 {
   2284   snd_seq_port_info_t *pinfo;
   2285   snd_seq_port_info_alloca( &pinfo );
   2286 
   2287   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2288   return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );
   2289 }
   2290 
   2291 std::string MidiOutAlsa :: getPortName( unsigned int portNumber )
   2292 {
   2293   snd_seq_client_info_t *cinfo;
   2294   snd_seq_port_info_t *pinfo;
   2295   snd_seq_client_info_alloca( &cinfo );
   2296   snd_seq_port_info_alloca( &pinfo );
   2297 
   2298   std::string stringName;
   2299   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2300   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
   2301     int cnum = snd_seq_port_info_get_client( pinfo );
   2302     snd_seq_get_any_client_info( data->seq, cnum, cinfo );
   2303     std::ostringstream os;
   2304     os << snd_seq_client_info_get_name( cinfo );
   2305     os << ":";
   2306     os << snd_seq_port_info_get_name( pinfo );
   2307     os << " ";                                    // These lines added to make sure devices are listed
   2308     os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names
   2309     os << ":";
   2310     os << snd_seq_port_info_get_port( pinfo );
   2311     stringName = os.str();
   2312     return stringName;
   2313   }
   2314 
   2315   // If we get here, we didn't find a match.
   2316   errorString_ = "MidiOutAlsa::getPortName: error looking for port name!";
   2317   error( RtMidiError::WARNING, errorString_ );
   2318   return stringName;
   2319 }
   2320 
   2321 void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName )
   2322 {
   2323   if ( connected_ ) {
   2324     errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!";
   2325     error( RtMidiError::WARNING, errorString_ );
   2326     return;
   2327   }
   2328 
   2329   unsigned int nSrc = this->getPortCount();
   2330   if ( nSrc < 1 ) {
   2331     errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!";
   2332     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
   2333     return;
   2334   }
   2335 
   2336   snd_seq_port_info_t *pinfo;
   2337   snd_seq_port_info_alloca( &pinfo );
   2338   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2339   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {
   2340     std::ostringstream ost;
   2341     ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
   2342     errorString_ = ost.str();
   2343     error( RtMidiError::INVALID_PARAMETER, errorString_ );
   2344     return;
   2345   }
   2346 
   2347   snd_seq_addr_t sender, receiver;
   2348   receiver.client = snd_seq_port_info_get_client( pinfo );
   2349   receiver.port = snd_seq_port_info_get_port( pinfo );
   2350   sender.client = snd_seq_client_id( data->seq );
   2351 
   2352   if ( data->vport < 0 ) {
   2353     data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
   2354                                               SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
   2355                                               SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
   2356     if ( data->vport < 0 ) {
   2357       errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port.";
   2358       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2359       return;
   2360     }
   2361   }
   2362 
   2363   sender.port = data->vport;
   2364 
   2365   // Make subscription
   2366   if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) {
   2367     snd_seq_port_subscribe_free( data->subscription );
   2368     errorString_ = "MidiOutAlsa::openPort: error allocating port subscription.";
   2369     error( RtMidiError::DRIVER_ERROR, errorString_ );
   2370     return;
   2371   }
   2372   snd_seq_port_subscribe_set_sender( data->subscription, &sender );
   2373   snd_seq_port_subscribe_set_dest( data->subscription, &receiver );
   2374   snd_seq_port_subscribe_set_time_update( data->subscription, 1 );
   2375   snd_seq_port_subscribe_set_time_real( data->subscription, 1 );
   2376   if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) {
   2377     snd_seq_port_subscribe_free( data->subscription );
   2378     errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection.";
   2379     error( RtMidiError::DRIVER_ERROR, errorString_ );
   2380     return;
   2381   }
   2382 
   2383   connected_ = true;
   2384 }
   2385 
   2386 void MidiOutAlsa :: closePort( void )
   2387 {
   2388   if ( connected_ ) {
   2389     AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2390     snd_seq_unsubscribe_port( data->seq, data->subscription );
   2391     snd_seq_port_subscribe_free( data->subscription );
   2392     data->subscription = 0;
   2393     connected_ = false;
   2394   }
   2395 }
   2396 
   2397 void MidiOutAlsa :: setClientName( const std::string &clientName )
   2398 {
   2399 
   2400     AlsaMidiData *data = static_cast<AlsaMidiData *> ( apiData_ );
   2401     snd_seq_set_client_name( data->seq, clientName.c_str() );
   2402 
   2403 }
   2404 
   2405 void MidiOutAlsa :: setPortName( const std::string &portName )
   2406 {
   2407   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2408   snd_seq_port_info_t *pinfo;
   2409   snd_seq_port_info_alloca( &pinfo );
   2410   snd_seq_get_port_info( data->seq, data->vport, pinfo );
   2411   snd_seq_port_info_set_name( pinfo, portName.c_str() );
   2412   snd_seq_set_port_info( data->seq, data->vport, pinfo );
   2413 }
   2414 
   2415 void MidiOutAlsa :: openVirtualPort( const std::string &portName )
   2416 {
   2417   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2418   if ( data->vport < 0 ) {
   2419     data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
   2420                                               SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
   2421                                               SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
   2422 
   2423     if ( data->vport < 0 ) {
   2424       errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port.";
   2425       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2426     }
   2427   }
   2428 }
   2429 
   2430 void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
   2431 {
   2432   long result;
   2433   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   2434   unsigned int nBytes = static_cast<unsigned int> (size);
   2435   if ( nBytes > data->bufferSize ) {
   2436     data->bufferSize = nBytes;
   2437     result = snd_midi_event_resize_buffer( data->coder, nBytes );
   2438     if ( result != 0 ) {
   2439       errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer.";
   2440       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2441       return;
   2442     }
   2443     free (data->buffer);
   2444     data->buffer = (unsigned char *) malloc( data->bufferSize );
   2445     if ( data->buffer == NULL ) {
   2446       errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
   2447       error( RtMidiError::MEMORY_ERROR, errorString_ );
   2448       return;
   2449     }
   2450   }
   2451 
   2452   for ( unsigned int i=0; i<nBytes; ++i ) data->buffer[i] = message[i];
   2453 
   2454   unsigned int offset = 0;
   2455   while (offset < nBytes) {
   2456     snd_seq_event_t ev;
   2457     snd_seq_ev_clear( &ev );
   2458     snd_seq_ev_set_source( &ev, data->vport );
   2459     snd_seq_ev_set_subs( &ev );
   2460     snd_seq_ev_set_direct( &ev );
   2461     result = snd_midi_event_encode( data->coder, data->buffer + offset,
   2462                                     (long)(nBytes - offset), &ev );
   2463     if ( result < 0 ) {
   2464       errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
   2465       error( RtMidiError::WARNING, errorString_ );
   2466       return;
   2467     }
   2468 
   2469     if ( ev.type == SND_SEQ_EVENT_NONE ) {
   2470       errorString_ = "MidiOutAlsa::sendMessage: incomplete message!";
   2471       error( RtMidiError::WARNING, errorString_ );
   2472       return;
   2473     }
   2474 
   2475     offset += result;
   2476 
   2477     // Send the event.
   2478     result = snd_seq_event_output( data->seq, &ev );
   2479     if ( result < 0 ) {
   2480       errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port.";
   2481       error( RtMidiError::WARNING, errorString_ );
   2482       return;
   2483     }
   2484   }
   2485   snd_seq_drain_output( data->seq );
   2486 }
   2487 
   2488 #endif // __LINUX_ALSA__
   2489 
   2490 
   2491 //*********************************************************************//
   2492 //  API: Windows Multimedia Library (MM)
   2493 //*********************************************************************//
   2494 
   2495 // API information deciphered from:
   2496 //  - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
   2497 
   2498 // Thanks to Jean-Baptiste Berruchon for the sysex code.
   2499 
   2500 #if defined(__WINDOWS_MM__)
   2501 
   2502 // The Windows MM API is based on the use of a callback function for
   2503 // MIDI input.  We convert the system specific time stamps to delta
   2504 // time values.
   2505 
   2506 // Windows MM MIDI header files.
   2507 #include <windows.h>
   2508 #include <mmsystem.h>
   2509 
   2510 // Convert a null-terminated wide string or ANSI-encoded string to UTF-8.
   2511 static std::string ConvertToUTF8(const TCHAR *str)
   2512 {
   2513   std::string u8str;
   2514   const WCHAR *wstr = L"";
   2515 #if defined( UNICODE ) || defined( _UNICODE )
   2516   wstr = str;
   2517 #else
   2518   // Convert from ANSI encoding to wide string
   2519   int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
   2520   std::wstring wstrtemp;
   2521   if ( wlength )
   2522   {
   2523     wstrtemp.assign( wlength - 1, 0 );
   2524     MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength );
   2525     wstr = &wstrtemp[0];
   2526   }
   2527 #endif
   2528   // Convert from wide string to UTF-8
   2529   int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL );
   2530   if ( length )
   2531   {
   2532     u8str.assign( length - 1, 0 );
   2533     length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL );
   2534   }
   2535   return u8str;
   2536 }
   2537 
   2538 // A structure to hold variables related to the CoreMIDI API
   2539 // implementation.
   2540 struct WinMidiData {
   2541   HMIDIIN inHandle;    // Handle to Midi Input Device
   2542   HMIDIOUT outHandle;  // Handle to Midi Output Device
   2543   DWORD lastTime;
   2544   MidiInApi::MidiMessage message;
   2545   std::vector<LPMIDIHDR> sysexBuffer;
   2546   CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
   2547 };
   2548 
   2549 //*********************************************************************//
   2550 //  API: Windows MM
   2551 //  Class Definitions: MidiInWinMM
   2552 //*********************************************************************//
   2553 
   2554 static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/,
   2555                                         UINT inputStatus,
   2556                                         DWORD_PTR instancePtr,
   2557                                         DWORD_PTR midiMessage,
   2558                                         DWORD timestamp )
   2559 {
   2560   if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;
   2561 
   2562   //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr);
   2563   MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr;
   2564   WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);
   2565 
   2566   // Calculate time stamp.
   2567   if ( data->firstMessage == true ) {
   2568     apiData->message.timeStamp = 0.0;
   2569     data->firstMessage = false;
   2570   }
   2571   else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;
   2572 
   2573   if ( inputStatus == MIM_DATA ) { // Channel or system message
   2574 
   2575     // Make sure the first byte is a status byte.
   2576     unsigned char status = (unsigned char) (midiMessage & 0x000000FF);
   2577     if ( !(status & 0x80) ) return;
   2578 
   2579     // Determine the number of bytes in the MIDI message.
   2580     unsigned short nBytes = 1;
   2581     if ( status < 0xC0 ) nBytes = 3;
   2582     else if ( status < 0xE0 ) nBytes = 2;
   2583     else if ( status < 0xF0 ) nBytes = 3;
   2584     else if ( status == 0xF1 ) {
   2585       if ( data->ignoreFlags & 0x02 ) return;
   2586       else nBytes = 2;
   2587     }
   2588     else if ( status == 0xF2 ) nBytes = 3;
   2589     else if ( status == 0xF3 ) nBytes = 2;
   2590     else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {
   2591       // A MIDI timing tick message and we're ignoring it.
   2592       return;
   2593     }
   2594     else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {
   2595       // A MIDI active sensing message and we're ignoring it.
   2596       return;
   2597     }
   2598 
   2599     // Copy bytes to our MIDI message.
   2600     unsigned char *ptr = (unsigned char *) &midiMessage;
   2601     for ( int i=0; i<nBytes; ++i ) apiData->message.bytes.push_back( *ptr++ );
   2602   }
   2603   else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )
   2604     MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
   2605     if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) {
   2606       // Sysex message and we're not ignoring it
   2607       for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i )
   2608         apiData->message.bytes.push_back( sysex->lpData[i] );
   2609     }
   2610 
   2611     // The WinMM API requires that the sysex buffer be requeued after
   2612     // input of each sysex message.  Even if we are ignoring sysex
   2613     // messages, we still need to requeue the buffer in case the user
   2614     // decides to not ignore sysex messages in the future.  However,
   2615     // it seems that WinMM calls this function with an empty sysex
   2616     // buffer when an application closes and in this case, we should
   2617     // avoid requeueing it, else the computer suddenly reboots after
   2618     // one or two minutes.
   2619     if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {
   2620       //if ( sysex->dwBytesRecorded > 0 ) {
   2621       EnterCriticalSection( &(apiData->_mutex) );
   2622       MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );
   2623       LeaveCriticalSection( &(apiData->_mutex) );
   2624       if ( result != MMSYSERR_NOERROR )
   2625         std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
   2626 
   2627       if ( data->ignoreFlags & 0x01 ) return;
   2628     }
   2629     else return;
   2630   }
   2631 
   2632   // Save the time of the last non-filtered message
   2633   apiData->lastTime = timestamp;
   2634 
   2635   if ( data->usingCallback ) {
   2636     RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
   2637     callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );
   2638   }
   2639   else {
   2640     // As long as we haven't reached our queue size limit, push the message.
   2641     if ( !data->queue.push( apiData->message ) )
   2642       std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n";
   2643   }
   2644 
   2645   // Clear the vector for the next input message.
   2646   apiData->message.bytes.clear();
   2647 }
   2648 
   2649 MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit )
   2650   : MidiInApi( queueSizeLimit )
   2651 {
   2652   MidiInWinMM::initialize( clientName );
   2653 }
   2654 
   2655 MidiInWinMM :: ~MidiInWinMM()
   2656 {
   2657   // Close a connection if it exists.
   2658   MidiInWinMM::closePort();
   2659 
   2660   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   2661   DeleteCriticalSection( &(data->_mutex) );
   2662 
   2663   // Cleanup.
   2664   delete data;
   2665 }
   2666 
   2667 void MidiInWinMM :: initialize( const std::string& /*clientName*/ )
   2668 {
   2669   // We'll issue a warning here if no devices are available but not
   2670   // throw an error since the user can plugin something later.
   2671   unsigned int nDevices = midiInGetNumDevs();
   2672   if ( nDevices == 0 ) {
   2673     errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available.";
   2674     error( RtMidiError::WARNING, errorString_ );
   2675   }
   2676 
   2677   // Save our api-specific connection information.
   2678   WinMidiData *data = (WinMidiData *) new WinMidiData;
   2679   apiData_ = (void *) data;
   2680   inputData_.apiData = (void *) data;
   2681   data->message.bytes.clear();  // needs to be empty for first input message
   2682 
   2683   if ( !InitializeCriticalSectionAndSpinCount( &(data->_mutex), 0x00000400 ) ) {
   2684     errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed.";
   2685     error( RtMidiError::WARNING, errorString_ );
   2686   }
   2687 }
   2688 
   2689 void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ )
   2690 {
   2691   if ( connected_ ) {
   2692     errorString_ = "MidiInWinMM::openPort: a valid connection already exists!";
   2693     error( RtMidiError::WARNING, errorString_ );
   2694     return;
   2695   }
   2696 
   2697   unsigned int nDevices = midiInGetNumDevs();
   2698   if (nDevices == 0) {
   2699     errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!";
   2700     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
   2701     return;
   2702   }
   2703 
   2704   if ( portNumber >= nDevices ) {
   2705     std::ostringstream ost;
   2706     ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
   2707     errorString_ = ost.str();
   2708     error( RtMidiError::INVALID_PARAMETER, errorString_ );
   2709     return;
   2710   }
   2711 
   2712   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   2713   MMRESULT result = midiInOpen( &data->inHandle,
   2714                                 portNumber,
   2715                                 (DWORD_PTR)&midiInputCallback,
   2716                                 (DWORD_PTR)&inputData_,
   2717                                 CALLBACK_FUNCTION );
   2718   if ( result != MMSYSERR_NOERROR ) {
   2719     errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port.";
   2720     error( RtMidiError::DRIVER_ERROR, errorString_ );
   2721     return;
   2722   }
   2723 
   2724   // Allocate and init the sysex buffers.
   2725   data->sysexBuffer.resize( inputData_.bufferCount );
   2726   for ( unsigned int i=0; i < inputData_.bufferCount; ++i ) {
   2727     data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
   2728     data->sysexBuffer[i]->lpData = new char[ inputData_.bufferSize ];
   2729     data->sysexBuffer[i]->dwBufferLength = inputData_.bufferSize;
   2730     data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
   2731     data->sysexBuffer[i]->dwFlags = 0;
   2732 
   2733     result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
   2734     if ( result != MMSYSERR_NOERROR ) {
   2735       midiInClose( data->inHandle );
   2736       data->inHandle = 0;
   2737       errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
   2738       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2739       return;
   2740     }
   2741 
   2742     // Register the buffer.
   2743     result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
   2744     if ( result != MMSYSERR_NOERROR ) {
   2745       midiInClose( data->inHandle );
   2746       data->inHandle = 0;
   2747       errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer).";
   2748       error( RtMidiError::DRIVER_ERROR, errorString_ );
   2749       return;
   2750     }
   2751   }
   2752 
   2753   result = midiInStart( data->inHandle );
   2754   if ( result != MMSYSERR_NOERROR ) {
   2755     midiInClose( data->inHandle );
   2756     data->inHandle = 0;
   2757     errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port.";
   2758     error( RtMidiError::DRIVER_ERROR, errorString_ );
   2759     return;
   2760   }
   2761 
   2762   connected_ = true;
   2763 }
   2764 
   2765 void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ )
   2766 {
   2767   // This function cannot be implemented for the Windows MM MIDI API.
   2768   errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
   2769   error( RtMidiError::WARNING, errorString_ );
   2770 }
   2771 
   2772 void MidiInWinMM :: closePort( void )
   2773 {
   2774   if ( connected_ ) {
   2775     WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   2776     EnterCriticalSection( &(data->_mutex) );
   2777     midiInReset( data->inHandle );
   2778     midiInStop( data->inHandle );
   2779 
   2780     for ( size_t i=0; i < data->sysexBuffer.size(); ++i ) {
   2781       int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
   2782       delete [] data->sysexBuffer[i]->lpData;
   2783       delete [] data->sysexBuffer[i];
   2784       if ( result != MMSYSERR_NOERROR ) {
   2785         midiInClose( data->inHandle );
   2786         data->inHandle = 0;
   2787         errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
   2788         error( RtMidiError::DRIVER_ERROR, errorString_ );
   2789         return;
   2790       }
   2791     }
   2792 
   2793     midiInClose( data->inHandle );
   2794     data->inHandle = 0;
   2795     connected_ = false;
   2796     LeaveCriticalSection( &(data->_mutex) );
   2797   }
   2798 }
   2799 
   2800 void MidiInWinMM :: setClientName ( const std::string& )
   2801 {
   2802 
   2803   errorString_ = "MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!";
   2804   error( RtMidiError::WARNING, errorString_ );
   2805 
   2806 }
   2807 
   2808 void MidiInWinMM :: setPortName ( const std::string& )
   2809 {
   2810 
   2811   errorString_ = "MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!";
   2812   error( RtMidiError::WARNING, errorString_ );
   2813 
   2814 }
   2815 
   2816 unsigned int MidiInWinMM :: getPortCount()
   2817 {
   2818   return midiInGetNumDevs();
   2819 }
   2820 
   2821 std::string MidiInWinMM :: getPortName( unsigned int portNumber )
   2822 {
   2823   std::string stringName;
   2824   unsigned int nDevices = midiInGetNumDevs();
   2825   if ( portNumber >= nDevices ) {
   2826     std::ostringstream ost;
   2827     ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
   2828     errorString_ = ost.str();
   2829     error( RtMidiError::WARNING, errorString_ );
   2830     return stringName;
   2831   }
   2832 
   2833   MIDIINCAPS deviceCaps;
   2834   midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
   2835   stringName = ConvertToUTF8( deviceCaps.szPname );
   2836 
   2837   // Next lines added to add the portNumber to the name so that
   2838   // the device's names are sure to be listed with individual names
   2839   // even when they have the same brand name
   2840 #ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
   2841   std::ostringstream os;
   2842   os << " ";
   2843   os << portNumber;
   2844   stringName += os.str();
   2845 #endif
   2846 
   2847   return stringName;
   2848 }
   2849 
   2850 //*********************************************************************//
   2851 //  API: Windows MM
   2852 //  Class Definitions: MidiOutWinMM
   2853 //*********************************************************************//
   2854 
   2855 MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi()
   2856 {
   2857   MidiOutWinMM::initialize( clientName );
   2858 }
   2859 
   2860 MidiOutWinMM :: ~MidiOutWinMM()
   2861 {
   2862   // Close a connection if it exists.
   2863   MidiOutWinMM::closePort();
   2864 
   2865   // Cleanup.
   2866   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   2867   delete data;
   2868 }
   2869 
   2870 void MidiOutWinMM :: initialize( const std::string& /*clientName*/ )
   2871 {
   2872   // We'll issue a warning here if no devices are available but not
   2873   // throw an error since the user can plug something in later.
   2874   unsigned int nDevices = midiOutGetNumDevs();
   2875   if ( nDevices == 0 ) {
   2876     errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available.";
   2877     error( RtMidiError::WARNING, errorString_ );
   2878   }
   2879 
   2880   // Save our api-specific connection information.
   2881   WinMidiData *data = (WinMidiData *) new WinMidiData;
   2882   apiData_ = (void *) data;
   2883 }
   2884 
   2885 unsigned int MidiOutWinMM :: getPortCount()
   2886 {
   2887   return midiOutGetNumDevs();
   2888 }
   2889 
   2890 std::string MidiOutWinMM :: getPortName( unsigned int portNumber )
   2891 {
   2892   std::string stringName;
   2893   unsigned int nDevices = midiOutGetNumDevs();
   2894   if ( portNumber >= nDevices ) {
   2895     std::ostringstream ost;
   2896     ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
   2897     errorString_ = ost.str();
   2898     error( RtMidiError::WARNING, errorString_ );
   2899     return stringName;
   2900   }
   2901 
   2902   MIDIOUTCAPS deviceCaps;
   2903   midiOutGetDevCaps( portNumber, &deviceCaps, sizeof( MIDIOUTCAPS ) );
   2904   stringName = ConvertToUTF8( deviceCaps.szPname );
   2905 
   2906   // Next lines added to add the portNumber to the name so that
   2907   // the device's names are sure to be listed with individual names
   2908   // even when they have the same brand name
   2909   std::ostringstream os;
   2910 #ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
   2911   os << " ";
   2912   os << portNumber;
   2913   stringName += os.str();
   2914 #endif
   2915 
   2916   return stringName;
   2917 }
   2918 
   2919 void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ )
   2920 {
   2921   if ( connected_ ) {
   2922     errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!";
   2923     error( RtMidiError::WARNING, errorString_ );
   2924     return;
   2925   }
   2926 
   2927   unsigned int nDevices = midiOutGetNumDevs();
   2928   if ( nDevices < 1 ) {
   2929     errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!";
   2930     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
   2931     return;
   2932   }
   2933 
   2934   if ( portNumber >= nDevices ) {
   2935     std::ostringstream ost;
   2936     ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
   2937     errorString_ = ost.str();
   2938     error( RtMidiError::INVALID_PARAMETER, errorString_ );
   2939     return;
   2940   }
   2941 
   2942   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   2943   MMRESULT result = midiOutOpen( &data->outHandle,
   2944                                  portNumber,
   2945                                  (DWORD)NULL,
   2946                                  (DWORD)NULL,
   2947                                  CALLBACK_NULL );
   2948   if ( result != MMSYSERR_NOERROR ) {
   2949     errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port.";
   2950     error( RtMidiError::DRIVER_ERROR, errorString_ );
   2951     return;
   2952   }
   2953 
   2954   connected_ = true;
   2955 }
   2956 
   2957 void MidiOutWinMM :: closePort( void )
   2958 {
   2959   if ( connected_ ) {
   2960     WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   2961     // Disabled because midiOutReset triggers 0x7b (if any note was ON) and 0x79 "Reset All
   2962     // Controllers" (to all 16 channels) CC messages which is undesirable (see issue #222)
   2963     // midiOutReset( data->outHandle );
   2964 
   2965     midiOutClose( data->outHandle );
   2966     data->outHandle = 0;
   2967     connected_ = false;
   2968   }
   2969 }
   2970 
   2971 void MidiOutWinMM :: setClientName ( const std::string& )
   2972 {
   2973 
   2974   errorString_ = "MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!";
   2975   error( RtMidiError::WARNING, errorString_ );
   2976 
   2977 }
   2978 
   2979 void MidiOutWinMM :: setPortName ( const std::string& )
   2980 {
   2981 
   2982   errorString_ = "MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!";
   2983   error( RtMidiError::WARNING, errorString_ );
   2984 
   2985 }
   2986 
   2987 void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ )
   2988 {
   2989   // This function cannot be implemented for the Windows MM MIDI API.
   2990   errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
   2991   error( RtMidiError::WARNING, errorString_ );
   2992 }
   2993 
   2994 void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size )
   2995 {
   2996   if ( !connected_ ) return;
   2997 
   2998   unsigned int nBytes = static_cast<unsigned int>(size);
   2999   if ( nBytes == 0 ) {
   3000     errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!";
   3001     error( RtMidiError::WARNING, errorString_ );
   3002     return;
   3003   }
   3004 
   3005   MMRESULT result;
   3006   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   3007   if ( message[0] == 0xF0 ) { // Sysex message
   3008 
   3009     // Allocate buffer for sysex data.
   3010     char *buffer = (char *) malloc( nBytes );
   3011     if ( buffer == NULL ) {
   3012       errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!";
   3013       error( RtMidiError::MEMORY_ERROR, errorString_ );
   3014       return;
   3015     }
   3016 
   3017     // Copy data to buffer.
   3018     for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message[i];
   3019 
   3020     // Create and prepare MIDIHDR structure.
   3021     MIDIHDR sysex;
   3022     sysex.lpData = (LPSTR) buffer;
   3023     sysex.dwBufferLength = nBytes;
   3024     sysex.dwFlags = 0;
   3025     result = midiOutPrepareHeader( data->outHandle,  &sysex, sizeof( MIDIHDR ) );
   3026     if ( result != MMSYSERR_NOERROR ) {
   3027       free( buffer );
   3028       errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header.";
   3029       error( RtMidiError::DRIVER_ERROR, errorString_ );
   3030       return;
   3031     }
   3032 
   3033     // Send the message.
   3034     result = midiOutLongMsg( data->outHandle, &sysex, sizeof( MIDIHDR ) );
   3035     if ( result != MMSYSERR_NOERROR ) {
   3036       free( buffer );
   3037       errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message.";
   3038       error( RtMidiError::DRIVER_ERROR, errorString_ );
   3039       return;
   3040     }
   3041 
   3042     // Unprepare the buffer and MIDIHDR.
   3043     while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof ( MIDIHDR ) ) ) Sleep( 1 );
   3044     free( buffer );
   3045   }
   3046   else { // Channel or system message.
   3047 
   3048     // Make sure the message size isn't too big.
   3049     if ( nBytes > 3 ) {
   3050       errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!";
   3051       error( RtMidiError::WARNING, errorString_ );
   3052       return;
   3053     }
   3054 
   3055     // Pack MIDI bytes into double word.
   3056     DWORD packet;
   3057     unsigned char *ptr = (unsigned char *) &packet;
   3058     for ( unsigned int i=0; i<nBytes; ++i ) {
   3059       *ptr = message[i];
   3060       ++ptr;
   3061     }
   3062 
   3063     // Send the message immediately.
   3064     result = midiOutShortMsg( data->outHandle, packet );
   3065     if ( result != MMSYSERR_NOERROR ) {
   3066       errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message.";
   3067       error( RtMidiError::DRIVER_ERROR, errorString_ );
   3068     }
   3069   }
   3070 }
   3071 
   3072 #endif  // __WINDOWS_MM__
   3073 
   3074 
   3075 //*********************************************************************//
   3076 //  API: UNIX JACK
   3077 //
   3078 //  Written primarily by Alexander Svetalkin, with updates for delta
   3079 //  time by Gary Scavone, April 2011.
   3080 //
   3081 //  *********************************************************************//
   3082 
   3083 #if defined(__UNIX_JACK__)
   3084 
   3085 // JACK header files
   3086 #include <jack/jack.h>
   3087 #include <jack/midiport.h>
   3088 #include <jack/ringbuffer.h>
   3089 #include <pthread.h>
   3090 #include <sched.h>
   3091 #ifdef HAVE_SEMAPHORE
   3092   #include <semaphore.h>
   3093 #endif
   3094 
   3095 #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
   3096 
   3097 struct JackMidiData {
   3098   jack_client_t *client;
   3099   jack_port_t *port;
   3100   jack_ringbuffer_t *buff;
   3101   int buffMaxWrite; // actual writable size, usually 1 less than ringbuffer
   3102   jack_time_t lastTime;
   3103 #ifdef HAVE_SEMAPHORE
   3104   sem_t sem_cleanup;
   3105   sem_t sem_needpost;
   3106 #endif
   3107   MidiInApi :: RtMidiInData *rtMidiIn;
   3108   };
   3109 
   3110 //*********************************************************************//
   3111 //  API: JACK
   3112 //  Class Definitions: MidiInJack
   3113 //*********************************************************************//
   3114 
   3115 static int jackProcessIn( jack_nframes_t nframes, void *arg )
   3116 {
   3117   JackMidiData *jData = (JackMidiData *) arg;
   3118   MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn;
   3119   jack_midi_event_t event;
   3120   jack_time_t time;
   3121 
   3122   // Is port created?
   3123   if ( jData->port == NULL ) return 0;
   3124 
   3125   void *buff = jack_port_get_buffer( jData->port, nframes );
   3126   bool& continueSysex = rtData->continueSysex;
   3127   unsigned char& ignoreFlags = rtData->ignoreFlags;
   3128 
   3129   // We have midi events in buffer
   3130   int evCount = jack_midi_get_event_count( buff );
   3131   for (int j = 0; j < evCount; j++) {
   3132     MidiInApi::MidiMessage& message = rtData->message;
   3133     jack_midi_event_get( &event, buff, j );
   3134 
   3135     // Compute the delta time.
   3136     time = jack_get_time();
   3137     if ( rtData->firstMessage == true ) {
   3138       message.timeStamp = 0.0;
   3139       rtData->firstMessage = false;
   3140     } else
   3141       message.timeStamp = ( time - jData->lastTime ) * 0.000001;
   3142 
   3143     jData->lastTime = time;
   3144 
   3145     if ( !continueSysex )
   3146       message.bytes.clear();
   3147 
   3148     if ( !( ( continueSysex || event.buffer[0] == 0xF0 ) && ( ignoreFlags & 0x01 ) ) ) {
   3149       // Unless this is a (possibly continued) SysEx message and we're ignoring SysEx,
   3150       // copy the event buffer into the MIDI message struct.
   3151       for ( unsigned int i = 0; i < event.size; i++ )
   3152         message.bytes.push_back( event.buffer[i] );
   3153     }
   3154 
   3155     switch ( event.buffer[0] ) {
   3156       case 0xF0:
   3157         // Start of a SysEx message
   3158         continueSysex = event.buffer[event.size - 1] != 0xF7;
   3159         if ( ignoreFlags & 0x01 ) continue;
   3160         break;
   3161       case 0xF1:
   3162       case 0xF8:
   3163         // MIDI Time Code or Timing Clock message
   3164         if ( ignoreFlags & 0x02 ) continue;
   3165         break;
   3166       case 0xFE:
   3167         // Active Sensing message
   3168         if ( ignoreFlags & 0x04 ) continue;
   3169         break;
   3170       default:
   3171         if ( continueSysex ) {
   3172           // Continuation of a SysEx message
   3173           continueSysex = event.buffer[event.size - 1] != 0xF7;
   3174           if ( ignoreFlags & 0x01 ) continue;
   3175         }
   3176         // All other MIDI messages
   3177     }
   3178 
   3179     if ( !continueSysex ) {
   3180       // If not a continuation of a SysEx message,
   3181       // invoke the user callback function or queue the message.
   3182       if ( rtData->usingCallback ) {
   3183         RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;
   3184         callback( message.timeStamp, &message.bytes, rtData->userData );
   3185       }
   3186       else {
   3187         // As long as we haven't reached our queue size limit, push the message.
   3188         if ( !rtData->queue.push( message ) )
   3189           std::cerr << "\nMidiInJack: message queue limit reached!!\n\n";
   3190       }
   3191     }
   3192   }
   3193 
   3194   return 0;
   3195 }
   3196 
   3197 MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit )
   3198   : MidiInApi( queueSizeLimit )
   3199 {
   3200   MidiInJack::initialize( clientName );
   3201 }
   3202 
   3203 void MidiInJack :: initialize( const std::string& clientName )
   3204 {
   3205   JackMidiData *data = new JackMidiData;
   3206   apiData_ = (void *) data;
   3207 
   3208   data->rtMidiIn = &inputData_;
   3209   data->port = NULL;
   3210   data->client = NULL;
   3211   this->clientName = clientName;
   3212 
   3213   connect();
   3214 }
   3215 
   3216 void MidiInJack :: connect()
   3217 {
   3218   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3219   if ( data->client )
   3220     return;
   3221 
   3222   // Initialize JACK client
   3223   if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
   3224     errorString_ = "MidiInJack::initialize: JACK server not running?";
   3225     error( RtMidiError::WARNING, errorString_ );
   3226     return;
   3227   }
   3228 
   3229   jack_set_process_callback( data->client, jackProcessIn, data );
   3230   jack_activate( data->client );
   3231 }
   3232 
   3233 MidiInJack :: ~MidiInJack()
   3234 {
   3235   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3236   MidiInJack::closePort();
   3237 
   3238   if ( data->client )
   3239     jack_client_close( data->client );
   3240   delete data;
   3241 }
   3242 
   3243 void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName )
   3244 {
   3245   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3246 
   3247   connect();
   3248 
   3249   // Creating new port
   3250   if ( data->port == NULL )
   3251     data->port = jack_port_register( data->client, portName.c_str(),
   3252                                      JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
   3253 
   3254   if ( data->port == NULL ) {
   3255     errorString_ = "MidiInJack::openPort: JACK error creating port";
   3256     if (portName.size() >= (size_t)jack_port_name_size())
   3257         errorString_ += " (port name too long?)";
   3258     error( RtMidiError::DRIVER_ERROR, errorString_ );
   3259     return;
   3260   }
   3261 
   3262   // Connecting to the output
   3263   std::string name = getPortName( portNumber );
   3264   jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );
   3265 
   3266   connected_ = true;
   3267 }
   3268 
   3269 void MidiInJack :: openVirtualPort( const std::string &portName )
   3270 {
   3271   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3272 
   3273   connect();
   3274   if ( data->port == NULL )
   3275     data->port = jack_port_register( data->client, portName.c_str(),
   3276                                      JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
   3277 
   3278   if ( data->port == NULL ) {
   3279     errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";
   3280     if (portName.size() >= (size_t)jack_port_name_size())
   3281         errorString_ += " (port name too long?)";
   3282     error( RtMidiError::DRIVER_ERROR, errorString_ );
   3283   }
   3284 }
   3285 
   3286 unsigned int MidiInJack :: getPortCount()
   3287 {
   3288   int count = 0;
   3289   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3290   connect();
   3291   if ( !data->client )
   3292     return 0;
   3293 
   3294   // List of available ports
   3295   const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
   3296 
   3297   if ( ports == NULL ) return 0;
   3298   while ( ports[count] != NULL )
   3299     count++;
   3300 
   3301   free( ports );
   3302 
   3303   return count;
   3304 }
   3305 
   3306 std::string MidiInJack :: getPortName( unsigned int portNumber )
   3307 {
   3308   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3309   std::string retStr( "" );
   3310 
   3311   connect();
   3312 
   3313   // List of available ports
   3314   const char **ports = jack_get_ports( data->client, NULL,
   3315                                        JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
   3316 
   3317   // Check port validity
   3318   if ( ports == NULL ) {
   3319     errorString_ = "MidiInJack::getPortName: no ports available!";
   3320     error( RtMidiError::WARNING, errorString_ );
   3321     return retStr;
   3322   }
   3323 
   3324   unsigned int i;
   3325   for ( i=0; i<portNumber && ports[i]; i++ ) {}
   3326   if ( i < portNumber || !ports[portNumber] ) {
   3327     std::ostringstream ost;
   3328     ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
   3329     errorString_ = ost.str();
   3330     error( RtMidiError::WARNING, errorString_ );
   3331   }
   3332   else retStr.assign( ports[portNumber] );
   3333 
   3334   jack_free( ports );
   3335   return retStr;
   3336 }
   3337 
   3338 void MidiInJack :: closePort()
   3339 {
   3340   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3341 
   3342   if ( data->port == NULL ) return;
   3343   jack_port_unregister( data->client, data->port );
   3344   data->port = NULL;
   3345 
   3346   connected_ = false;
   3347 }
   3348 
   3349 void MidiInJack:: setClientName( const std::string& )
   3350 {
   3351 
   3352   errorString_ = "MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API!";
   3353   error( RtMidiError::WARNING, errorString_ );
   3354 
   3355 }
   3356 
   3357 void MidiInJack :: setPortName( const std::string &portName )
   3358 {
   3359   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3360 #ifdef JACK_HAS_PORT_RENAME
   3361   jack_port_rename( data->client, data->port, portName.c_str() );
   3362 #else
   3363   jack_port_set_name( data->port, portName.c_str() );
   3364 #endif
   3365 }
   3366 
   3367 //*********************************************************************//
   3368 //  API: JACK
   3369 //  Class Definitions: MidiOutJack
   3370 //*********************************************************************//
   3371 
   3372 // Jack process callback
   3373 static int jackProcessOut( jack_nframes_t nframes, void *arg )
   3374 {
   3375   JackMidiData *data = (JackMidiData *) arg;
   3376   jack_midi_data_t *midiData;
   3377   int space;
   3378 
   3379   // Is port created?
   3380   if ( data->port == NULL ) return 0;
   3381 
   3382   void *buff = jack_port_get_buffer( data->port, nframes );
   3383   jack_midi_clear_buffer( buff );
   3384 
   3385   while ( jack_ringbuffer_peek( data->buff, (char *) &space, sizeof( space ) ) == sizeof(space) &&
   3386           jack_ringbuffer_read_space( data->buff ) >= sizeof(space) + space ) {
   3387     jack_ringbuffer_read_advance( data->buff, sizeof(space) );
   3388 
   3389     midiData = jack_midi_event_reserve( buff, 0, space );
   3390     if ( midiData )
   3391         jack_ringbuffer_read( data->buff, (char *) midiData, (size_t) space );
   3392     else
   3393         jack_ringbuffer_read_advance( data->buff, (size_t) space );
   3394   }
   3395 
   3396 #ifdef HAVE_SEMAPHORE
   3397   if ( !sem_trywait( &data->sem_needpost ) )
   3398     sem_post( &data->sem_cleanup );
   3399 #endif
   3400 
   3401   return 0;
   3402 }
   3403 
   3404 MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi()
   3405 {
   3406   MidiOutJack::initialize( clientName );
   3407 }
   3408 
   3409 void MidiOutJack :: initialize( const std::string& clientName )
   3410 {
   3411   JackMidiData *data = new JackMidiData;
   3412   apiData_ = (void *) data;
   3413 
   3414   data->port = NULL;
   3415   data->client = NULL;
   3416 #ifdef HAVE_SEMAPHORE
   3417   sem_init( &data->sem_cleanup, 0, 0 );
   3418   sem_init( &data->sem_needpost, 0, 0 );
   3419 #endif
   3420   this->clientName = clientName;
   3421 
   3422   connect();
   3423 }
   3424 
   3425 void MidiOutJack :: connect()
   3426 {
   3427   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3428   if ( data->client )
   3429     return;
   3430 
   3431   // Initialize output ringbuffers
   3432   data->buff = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
   3433   data->buffMaxWrite = (int) jack_ringbuffer_write_space( data->buff );
   3434 
   3435   // Initialize JACK client
   3436   if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) {
   3437     errorString_ = "MidiOutJack::initialize: JACK server not running?";
   3438     error( RtMidiError::WARNING, errorString_ );
   3439     return;
   3440   }
   3441 
   3442   jack_set_process_callback( data->client, jackProcessOut, data );
   3443   jack_activate( data->client );
   3444 }
   3445 
   3446 MidiOutJack :: ~MidiOutJack()
   3447 {
   3448   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3449   MidiOutJack::closePort();
   3450 
   3451   // Cleanup
   3452   jack_ringbuffer_free( data->buff );
   3453   if ( data->client ) {
   3454     jack_client_close( data->client );
   3455   }
   3456 
   3457 #ifdef HAVE_SEMAPHORE
   3458   sem_destroy( &data->sem_cleanup );
   3459   sem_destroy( &data->sem_needpost );
   3460 #endif
   3461 
   3462   delete data;
   3463 }
   3464 
   3465 void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName )
   3466 {
   3467   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3468 
   3469   connect();
   3470 
   3471   // Creating new port
   3472   if ( data->port == NULL )
   3473     data->port = jack_port_register( data->client, portName.c_str(),
   3474                                      JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
   3475 
   3476   if ( data->port == NULL ) {
   3477     errorString_ = "MidiOutJack::openPort: JACK error creating port";
   3478     if (portName.size() >= (size_t)jack_port_name_size())
   3479         errorString_ += " (port name too long?)";
   3480     error( RtMidiError::DRIVER_ERROR, errorString_ );
   3481     return;
   3482   }
   3483 
   3484   // Connecting to the output
   3485   std::string name = getPortName( portNumber );
   3486   jack_connect( data->client, jack_port_name( data->port ), name.c_str() );
   3487 
   3488   connected_ = true;
   3489 }
   3490 
   3491 void MidiOutJack :: openVirtualPort( const std::string &portName )
   3492 {
   3493   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3494 
   3495   connect();
   3496   if ( data->port == NULL )
   3497     data->port = jack_port_register( data->client, portName.c_str(),
   3498                                      JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
   3499 
   3500   if ( data->port == NULL ) {
   3501     errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";
   3502     if (portName.size() >= (size_t)jack_port_name_size())
   3503         errorString_ += " (port name too long?)";
   3504     error( RtMidiError::DRIVER_ERROR, errorString_ );
   3505   }
   3506 }
   3507 
   3508 unsigned int MidiOutJack :: getPortCount()
   3509 {
   3510   int count = 0;
   3511   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3512   connect();
   3513   if ( !data->client )
   3514     return 0;
   3515 
   3516   // List of available ports
   3517   const char **ports = jack_get_ports( data->client, NULL,
   3518                                        JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
   3519 
   3520   if ( ports == NULL ) return 0;
   3521   while ( ports[count] != NULL )
   3522     count++;
   3523 
   3524   free( ports );
   3525 
   3526   return count;
   3527 }
   3528 
   3529 std::string MidiOutJack :: getPortName( unsigned int portNumber )
   3530 {
   3531   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3532   std::string retStr("");
   3533 
   3534   connect();
   3535 
   3536   // List of available ports
   3537   const char **ports = jack_get_ports( data->client, NULL,
   3538                                        JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
   3539 
   3540   // Check port validity
   3541   if ( ports == NULL ) {
   3542     errorString_ = "MidiOutJack::getPortName: no ports available!";
   3543     error( RtMidiError::WARNING, errorString_ );
   3544     return retStr;
   3545   }
   3546 
   3547   if ( ports[portNumber] == NULL ) {
   3548     std::ostringstream ost;
   3549     ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
   3550     errorString_ = ost.str();
   3551     error( RtMidiError::WARNING, errorString_ );
   3552   }
   3553   else retStr.assign( ports[portNumber] );
   3554 
   3555   free( ports );
   3556   return retStr;
   3557 }
   3558 
   3559 void MidiOutJack :: closePort()
   3560 {
   3561   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3562 
   3563   if ( data->port == NULL ) return;
   3564 
   3565 #ifdef HAVE_SEMAPHORE
   3566   struct timespec ts;
   3567   if ( clock_gettime( CLOCK_REALTIME, &ts ) != -1 ) {
   3568     ts.tv_sec += 1; // wait max one second
   3569     sem_post( &data->sem_needpost );
   3570     sem_timedwait( &data->sem_cleanup, &ts );
   3571   }
   3572 #endif
   3573 
   3574   jack_port_unregister( data->client, data->port );
   3575   data->port = NULL;
   3576 
   3577   connected_ = false;
   3578 }
   3579 
   3580 void MidiOutJack:: setClientName( const std::string& )
   3581 {
   3582 
   3583   errorString_ = "MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API!";
   3584   error( RtMidiError::WARNING, errorString_ );
   3585 
   3586 }
   3587 
   3588 void MidiOutJack :: setPortName( const std::string &portName )
   3589 {
   3590   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3591 #ifdef JACK_HAS_PORT_RENAME
   3592   jack_port_rename( data->client, data->port, portName.c_str() );
   3593 #else
   3594   jack_port_set_name( data->port, portName.c_str() );
   3595 #endif
   3596 }
   3597 
   3598 void MidiOutJack :: sendMessage( const unsigned char *message, size_t size )
   3599 {
   3600   int nBytes = static_cast<int>(size);
   3601   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
   3602 
   3603   if ( size + sizeof(nBytes) > (size_t) data->buffMaxWrite )
   3604       return;
   3605 
   3606   while ( jack_ringbuffer_write_space(data->buff) < sizeof(nBytes) + size )
   3607       sched_yield();
   3608 
   3609   // Write full message to buffer
   3610   jack_ringbuffer_write( data->buff, ( char * ) &nBytes, sizeof( nBytes ) );
   3611   jack_ringbuffer_write( data->buff, ( const char * ) message, nBytes );
   3612 }
   3613 
   3614 #endif  // __UNIX_JACK__
   3615 
   3616 //*********************************************************************//
   3617 //  API: Web MIDI
   3618 //
   3619 //  Written primarily by Atsushi Eno, February 2020.
   3620 //
   3621 //  *********************************************************************//
   3622 
   3623 #if defined(__WEB_MIDI_API__)
   3624 
   3625 #include <emscripten.h>
   3626 
   3627 //*********************************************************************//
   3628 //  API: WEB MIDI
   3629 //  Class Definitions: WebMidiAccessShim
   3630 //*********************************************************************//
   3631 
   3632 class WebMidiAccessShim
   3633 {
   3634 public:
   3635   WebMidiAccessShim();
   3636   ~WebMidiAccessShim();
   3637   std::string getPortName( unsigned int portNumber, bool isInput );
   3638 };
   3639 
   3640 std::unique_ptr<WebMidiAccessShim> shim{nullptr};
   3641 
   3642 void ensureShim()
   3643 {
   3644   if ( shim.get() != nullptr )
   3645     return;
   3646   shim.reset( new WebMidiAccessShim() );
   3647 }
   3648 
   3649 bool checkWebMidiAvailability()
   3650 {
   3651   ensureShim();
   3652 
   3653   return MAIN_THREAD_EM_ASM_INT( {
   3654     if ( typeof window._rtmidi_internals_waiting === "undefined" ) {
   3655       console.log ( "Attempted to use Web MIDI API without trying to open it." );
   3656       return false;
   3657     }
   3658     if ( window._rtmidi_internals_waiting ) {
   3659       console.log ( "Attempted to use Web MIDI API while it is being queried." );
   3660       return false;
   3661     }
   3662     if ( _rtmidi_internals_midi_access == null ) {
   3663       console.log ( "Attempted to use Web MIDI API while it already turned out to be unavailable." );
   3664       return false;
   3665     }
   3666     return true;
   3667   } );
   3668 }
   3669 
   3670 WebMidiAccessShim::WebMidiAccessShim()
   3671 {
   3672   MAIN_THREAD_ASYNC_EM_ASM( {
   3673     if( typeof window._rtmidi_internals_midi_access !== "undefined" )
   3674       return;
   3675     if( typeof window._rtmidi_internals_waiting !== "undefined" ) {
   3676        console.log( "MIDI Access was requested while another request is in progress." );
   3677        return;
   3678     }
   3679 
   3680     // define functions
   3681     window._rtmidi_internals_get_port_by_number = function( portNumber, isInput ) {
   3682       var midi = window._rtmidi_internals_midi_access;
   3683       var devices = isInput ? midi.inputs : midi.outputs;
   3684       var i = 0;
   3685       for (var device of devices.values()) {
   3686         if ( i == portNumber )
   3687           return device;
   3688         i++;
   3689       }
   3690       console.log( "MIDI " + (isInput ? "input" : "output") + " device of portNumber " + portNumber + " is not found.");
   3691       return null;
   3692     };
   3693 
   3694     window._rtmidi_internals_waiting = true;
   3695     window.navigator.requestMIDIAccess( {"sysex": true} ).then( (midiAccess) => {
   3696       window._rtmidi_internals_midi_access = midiAccess;
   3697       window._rtmidi_internals_latest_message_timestamp = 0.0;
   3698       window._rtmidi_internals_waiting = false;
   3699       if( midiAccess == null ) {
   3700         console.log ( "Could not get access to MIDI API" );
   3701       }
   3702     } );
   3703   } );
   3704 }
   3705 
   3706 WebMidiAccessShim::~WebMidiAccessShim()
   3707 {
   3708 }
   3709 
   3710 std::string WebMidiAccessShim::getPortName( unsigned int portNumber, bool isInput )
   3711 {
   3712   if( !checkWebMidiAvailability() )
   3713     return "";
   3714   char *ret = (char*) MAIN_THREAD_EM_ASM_INT( {
   3715     var port = window._rtmidi_internals_get_port_by_number($0, $1);
   3716     if( port == null)
   3717       return null;
   3718     var length = lengthBytesUTF8(port.name) + 1;
   3719     var ret = _malloc(length);
   3720     stringToUTF8(port.name, ret, length);
   3721     return ret;
   3722   }, portNumber, isInput);
   3723   if (ret == nullptr)
   3724       return "";
   3725   std::string s = ret;
   3726   free(ret);
   3727   return s;
   3728 }
   3729 
   3730 //*********************************************************************//
   3731 //  API: WEB MIDI
   3732 //  Class Definitions: MidiInWeb
   3733 //*********************************************************************//
   3734 
   3735 MidiInWeb::MidiInWeb( const std::string &clientName, unsigned int queueSizeLimit )
   3736   : MidiInApi( queueSizeLimit )
   3737 {
   3738   initialize( clientName );
   3739 }
   3740 
   3741 MidiInWeb::~MidiInWeb( void )
   3742 {
   3743   closePort();
   3744 }
   3745 
   3746 extern "C" void EMSCRIPTEN_KEEPALIVE rtmidi_onMidiMessageProc( MidiInApi::RtMidiInData* data, uint8_t* inputBytes, int32_t length, double domHighResTimeStamp )
   3747 {
   3748   auto &message = data->message;
   3749   message.bytes.resize(message.bytes.size() + length);
   3750   memcpy(message.bytes.data(), inputBytes, length);
   3751   // FIXME: handle timestamp
   3752   if ( data->usingCallback ) {
   3753     RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
   3754     callback( message.timeStamp, &message.bytes, data->userData );
   3755   }
   3756 }
   3757 
   3758 void MidiInWeb::openPort( unsigned int portNumber, const std::string &portName )
   3759 {
   3760   if( !checkWebMidiAvailability() )
   3761     return;
   3762   if (open_port_number >= 0)
   3763     return;
   3764 
   3765   MAIN_THREAD_EM_ASM( {
   3766     // In Web MIDI API world, there is no step to open a port, but we have to register the input callback instead.
   3767     var input = window._rtmidi_internals_get_port_by_number($0, true);
   3768     input.onmidimessage = function(e) {
   3769       // In RtMidi world, timestamps are delta time from previous message, while in Web MIDI world
   3770       // timestamps are relative to window creation time (i.e. kind of absolute time with window "epoch" time).
   3771       var rtmidiTimestamp = window._rtmidi_internals_latest_message_timestamp == 0.0 ? 0.0 : e.timeStamp - window._rtmidi_internals_latest_message_timestamp;
   3772       window._rtmidi_internals_latest_message_timestamp = e.timeStamp;
   3773       Module.ccall( 'rtmidi_onMidiMessageProc', 'void', ['number', 'array', 'number', 'number'], [$1, e.data, e.data.length, rtmidiTimestamp] );
   3774     };
   3775   }, portNumber, &inputData_ );
   3776   open_port_number = portNumber;
   3777 }
   3778 
   3779 void MidiInWeb::openVirtualPort( const std::string &portName )
   3780 {
   3781 
   3782   errorString_ = "MidiInWeb::openVirtualPort: this function is not implemented for the Web MIDI API!";
   3783   error( RtMidiError::WARNING, errorString_ );
   3784 
   3785 }
   3786 
   3787 void MidiInWeb::closePort( void )
   3788 {
   3789   if( open_port_number < 0 )
   3790     return;
   3791 
   3792   MAIN_THREAD_EM_ASM( {
   3793     var input = _rtmidi_internals_get_port_by_number($0, true);
   3794     if( input == null ) {
   3795       console.log( "Port #" + $0 + " could not be found.");
   3796       return;
   3797     }
   3798     // unregister event handler
   3799     input.onmidimessage = null;
   3800   }, open_port_number );
   3801   open_port_number = -1;
   3802 }
   3803 
   3804 void MidiInWeb::setClientName( const std::string &clientName )
   3805 {
   3806   client_name = clientName;
   3807 }
   3808 
   3809 void MidiInWeb::setPortName( const std::string &portName )
   3810 {
   3811 
   3812   errorString_ = "MidiInWeb::setPortName: this function is not implemented for the Web MIDI API!";
   3813   error( RtMidiError::WARNING, errorString_ );
   3814 
   3815 }
   3816 
   3817 unsigned int MidiInWeb::getPortCount( void )
   3818 {
   3819   if( !checkWebMidiAvailability() )
   3820     return 0;
   3821   return MAIN_THREAD_EM_ASM_INT( { return _rtmidi_internals_midi_access.inputs.size; } );
   3822 }
   3823 
   3824 std::string MidiInWeb::getPortName( unsigned int portNumber )
   3825 {
   3826   if( !checkWebMidiAvailability() )
   3827     return "";
   3828   return shim->getPortName( portNumber, true );
   3829 }
   3830 
   3831 void MidiInWeb::initialize( const std::string& clientName )
   3832 {
   3833   ensureShim();
   3834   setClientName( clientName );
   3835 }
   3836 
   3837 //*********************************************************************//
   3838 //  API: WEB MIDI
   3839 //  Class Definitions: MidiOutWeb
   3840 //*********************************************************************//
   3841 
   3842 MidiOutWeb::MidiOutWeb( const std::string &clientName )
   3843 {
   3844   initialize( clientName );
   3845 }
   3846 
   3847 MidiOutWeb::~MidiOutWeb( void )
   3848 {
   3849   closePort();
   3850 }
   3851 
   3852 void MidiOutWeb::openPort( unsigned int portNumber, const std::string &portName )
   3853 {
   3854   if( !checkWebMidiAvailability() )
   3855     return;
   3856   if (open_port_number >= 0)
   3857     return;
   3858   // In Web MIDI API world, there is no step to open a port.
   3859 
   3860   open_port_number = portNumber;
   3861 }
   3862 
   3863 void MidiOutWeb::openVirtualPort( const std::string &portName )
   3864 {
   3865 
   3866   errorString_ = "MidiOutWeb::openVirtualPort: this function is not implemented for the Web MIDI API!";
   3867   error( RtMidiError::WARNING, errorString_ );
   3868 
   3869 }
   3870 
   3871 void MidiOutWeb::closePort( void )
   3872 {
   3873   // there is really nothing to do for output at JS side.
   3874   open_port_number = -1;
   3875 }
   3876 
   3877 void MidiOutWeb::setClientName( const std::string &clientName )
   3878 {
   3879   client_name = clientName;
   3880 }
   3881 
   3882 void MidiOutWeb::setPortName( const std::string &portName )
   3883 {
   3884 
   3885   errorString_ = "MidiOutWeb::setPortName: this function is not implemented for the Web MIDI API!";
   3886   error( RtMidiError::WARNING, errorString_ );
   3887 
   3888 }
   3889 
   3890 unsigned int MidiOutWeb::getPortCount( void )
   3891 {
   3892   if( !checkWebMidiAvailability() )
   3893     return 0;
   3894   return MAIN_THREAD_EM_ASM_INT( { return _rtmidi_internals_midi_access.outputs.size; } );
   3895 }
   3896 
   3897 std::string MidiOutWeb::getPortName( unsigned int portNumber )
   3898 {
   3899   if( !checkWebMidiAvailability() )
   3900     return "";
   3901   return shim->getPortName( portNumber, false );
   3902 }
   3903 
   3904 void MidiOutWeb::sendMessage( const unsigned char *message, size_t size )
   3905 {
   3906   if( open_port_number < 0 )
   3907     return;
   3908 
   3909   MAIN_THREAD_EM_ASM( {
   3910     var output = _rtmidi_internals_get_port_by_number( $0, false );
   3911     if( output == null ) {
   3912       console.log( "Port #" + $0 + " could not be found.");
   3913       return;
   3914     }
   3915     var buf = new ArrayBuffer ($2);
   3916     var msg = new Uint8Array( buf );
   3917     msg.set( new Uint8Array( Module.HEAPU8.buffer.slice( $1, $1 + $2 ) ) );
   3918     output.send( msg );
   3919   }, open_port_number, message, size );
   3920 }
   3921 
   3922 void MidiOutWeb::initialize( const std::string& clientName )
   3923 {
   3924   if ( shim.get() != nullptr )
   3925     return;
   3926   shim.reset( new WebMidiAccessShim() );
   3927   setClientName( clientName );
   3928 }
   3929 
   3930 #endif  // __WEB_MIDI_API__