PageRenderTime 171ms CodeModel.GetById 29ms app.highlight 126ms RepoModel.GetById 1ms app.codeStats 1ms

/RtMidi.cpp

http://ewitool.googlecode.com/
C++ | 3747 lines | 2794 code | 575 blank | 378 comment | 588 complexity | b595065c57d234c35379646fa077e891 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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 WWW site: http://music.mcgill.ca/~gary/rtmidi/
   9
  10    RtMidi: realtime MIDI i/o C++ classes
  11    Copyright (c) 2003-2012 Gary P. Scavone
  12
  13    Permission is hereby granted, free of charge, to any person
  14    obtaining a copy of this software and associated documentation files
  15    (the "Software"), to deal in the Software without restriction,
  16    including without limitation the rights to use, copy, modify, merge,
  17    publish, distribute, sublicense, and/or sell copies of the Software,
  18    and to permit persons to whom the Software is furnished to do so,
  19    subject to the following conditions:
  20
  21    The above copyright notice and this permission notice shall be
  22    included in all copies or substantial portions of the Software.
  23
  24    Any person wishing to distribute modifications to the Software is
  25    asked to send the modifications to the original developer so that
  26    they can be incorporated into the canonical version.  This is,
  27    however, not a binding provision of this license.
  28
  29    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  30    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  31    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  32    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
  33    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  34    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  35    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  36*/
  37/**********************************************************************/
  38
  39// RtMidi: Version 2.0.1
  40
  41#include "RtMidi.h"
  42#include <sstream>
  43
  44//*********************************************************************//
  45//  RtMidi Definitions
  46//*********************************************************************//
  47
  48void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()
  49{
  50  apis.clear();
  51
  52  // The order here will control the order of RtMidi's API search in
  53  // the constructor.
  54#if defined(__MACOSX_CORE__)
  55  apis.push_back( MACOSX_CORE );
  56#endif
  57#if defined(__LINUX_ALSA__)
  58  apis.push_back( LINUX_ALSA );
  59#endif
  60#if defined(__UNIX_JACK__)
  61  apis.push_back( UNIX_JACK );
  62#endif
  63#if defined(__WINDOWS_MM__)
  64  apis.push_back( WINDOWS_MM );
  65#endif
  66#if defined(__WINDOWS_KS__)
  67  apis.push_back( WINDOWS_KS );
  68#endif
  69#if defined(__RTMIDI_DUMMY__)
  70  apis.push_back( RTMIDI_DUMMY );
  71#endif
  72}
  73
  74void RtMidi :: error( RtError::Type type, std::string errorString )
  75{
  76  if (type == RtError::WARNING) {
  77    std::cerr << '\n' << errorString << "\n\n";
  78  }
  79  else if (type == RtError::DEBUG_WARNING) {
  80#if defined(__RTMIDI_DEBUG__)
  81    std::cerr << '\n' << errorString << "\n\n";
  82#endif
  83  }
  84  else {
  85    std::cerr << '\n' << errorString << "\n\n";
  86    throw RtError( errorString, type );
  87  }
  88}
  89
  90//*********************************************************************//
  91//  RtMidiIn Definitions
  92//*********************************************************************//
  93
  94void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit )
  95{
  96  if ( rtapi_ )
  97    delete rtapi_;
  98  rtapi_ = 0;
  99
 100#if defined(__UNIX_JACK__)
 101  if ( api == UNIX_JACK )
 102    rtapi_ = new MidiInJack( clientName, queueSizeLimit );
 103#endif
 104#if defined(__LINUX_ALSA__)
 105  if ( api == LINUX_ALSA )
 106    rtapi_ = new MidiInAlsa( clientName, queueSizeLimit );
 107#endif
 108#if defined(__WINDOWS_MM__)
 109  if ( api == WINDOWS_MM )
 110    rtapi_ = new MidiInWinMM( clientName, queueSizeLimit );
 111#endif
 112#if defined(__WINDOWS_KS__)
 113  if ( api == WINDOWS_KS )
 114    rtapi_ = new MidiInWinKS( clientName, queueSizeLimit );
 115#endif
 116#if defined(__MACOSX_CORE__)
 117  if ( api == MACOSX_CORE )
 118    rtapi_ = new MidiInCore( clientName, queueSizeLimit );
 119#endif
 120#if defined(__RTMIDI_DUMMY__)
 121  if ( api == RTMIDI_DUMMY )
 122    rtapi_ = new MidiInDummy( clientName, queueSizeLimit );
 123#endif
 124}
 125
 126RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit )
 127{
 128  rtapi_ = 0;
 129
 130  if ( api != UNSPECIFIED ) {
 131    // Attempt to open the specified API.
 132    openMidiApi( api, clientName, queueSizeLimit );
 133    if ( rtapi_ ) return;
 134
 135    // No compiled support for specified API value.  Issue a debug
 136    // warning and continue as if no API was specified.
 137    RtMidi::error( RtError::WARNING, "RtMidiIn: no compiled support for specified API argument!" );
 138  }
 139
 140  // Iterate through the compiled APIs and return as soon as we find
 141  // one with at least one port or we reach the end of the list.
 142  std::vector< RtMidi::Api > apis;
 143  getCompiledApi( apis );
 144  for ( unsigned int i=0; i<apis.size(); i++ ) {
 145    openMidiApi( apis[i], clientName, queueSizeLimit );
 146    if ( rtapi_->getPortCount() ) break;
 147  }
 148
 149  if ( rtapi_ ) return;
 150
 151  // It should not be possible to get here because the preprocessor
 152  // definition __RTMIDI_DUMMY__ is automatically defined if no
 153  // API-specific definitions are passed to the compiler. But just in
 154  // case something weird happens, we'll print out an error message.
 155  RtMidi::error( RtError::WARNING, "RtMidiIn: no compiled API support found ... critical error!!" );
 156}
 157
 158RtMidiIn :: ~RtMidiIn() throw()
 159{
 160  delete rtapi_;
 161}
 162
 163
 164//*********************************************************************//
 165//  RtMidiOut Definitions
 166//*********************************************************************//
 167
 168void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName )
 169{
 170  if ( rtapi_ )
 171    delete rtapi_;
 172  rtapi_ = 0;
 173
 174#if defined(__UNIX_JACK__)
 175  if ( api == UNIX_JACK )
 176    rtapi_ = new MidiOutJack( clientName );
 177#endif
 178#if defined(__LINUX_ALSA__)
 179  if ( api == LINUX_ALSA )
 180    rtapi_ = new MidiOutAlsa( clientName );
 181#endif
 182#if defined(__WINDOWS_MM__)
 183  if ( api == WINDOWS_MM )
 184    rtapi_ = new MidiOutWinMM( clientName );
 185#endif
 186#if defined(__WINDOWS_KS__)
 187  if ( api == WINDOWS_KS )
 188    rtapi_ = new MidiOutWinKS( clientName );
 189#endif
 190#if defined(__MACOSX_CORE__)
 191  if ( api == MACOSX_CORE )
 192    rtapi_ = new MidiOutCore( clientName );
 193#endif
 194#if defined(__RTMIDI_DUMMY__)
 195  if ( api == RTMIDI_DUMMY )
 196    rtapi_ = new MidiOutDummy( clientName );
 197#endif
 198}
 199
 200RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName )
 201{
 202  rtapi_ = 0;
 203
 204  if ( api != UNSPECIFIED ) {
 205    // Attempt to open the specified API.
 206    openMidiApi( api, clientName );
 207    if ( rtapi_ ) return;
 208
 209    // No compiled support for specified API value.  Issue a debug
 210    // warning and continue as if no API was specified.
 211    RtMidi::error( RtError::WARNING, "RtMidiOut: no compiled support for specified API argument!" );
 212  }
 213
 214  // Iterate through the compiled APIs and return as soon as we find
 215  // one with at least one port or we reach the end of the list.
 216  std::vector< RtMidi::Api > apis;
 217  getCompiledApi( apis );
 218  for ( unsigned int i=0; i<apis.size(); i++ ) {
 219    openMidiApi( apis[i], clientName );
 220    if ( rtapi_->getPortCount() ) break;
 221  }
 222
 223  if ( rtapi_ ) return;
 224
 225  // It should not be possible to get here because the preprocessor
 226  // definition __RTMIDI_DUMMY__ is automatically defined if no
 227  // API-specific definitions are passed to the compiler. But just in
 228  // case something weird happens, we'll print out an error message.
 229  RtMidi::error( RtError::WARNING, "RtMidiOut: no compiled API support found ... critical error!!" );
 230}
 231
 232RtMidiOut :: ~RtMidiOut() throw()
 233{
 234  delete rtapi_;
 235}
 236
 237//*********************************************************************//
 238//  Common MidiInApi Definitions
 239//*********************************************************************//
 240
 241MidiInApi :: MidiInApi( unsigned int queueSizeLimit )
 242  : apiData_( 0 ), connected_( false )
 243{
 244  // Allocate the MIDI queue.
 245  inputData_.queue.ringSize = queueSizeLimit;
 246  if ( inputData_.queue.ringSize > 0 )
 247    inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ];
 248}
 249
 250MidiInApi :: ~MidiInApi( void )
 251{
 252  // Delete the MIDI queue.
 253  if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring;
 254}
 255
 256void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData )
 257{
 258  if ( inputData_.usingCallback ) {
 259    errorString_ = "MidiInApi::setCallback: a callback function is already set!";
 260    RtMidi::error( RtError::WARNING, errorString_ );
 261    return;
 262  }
 263
 264  if ( !callback ) {
 265    errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";
 266    RtMidi::error( RtError::WARNING, errorString_ );
 267    return;
 268  }
 269
 270  inputData_.userCallback = (void *) callback;
 271  inputData_.userData = userData;
 272  inputData_.usingCallback = true;
 273}
 274
 275void MidiInApi :: cancelCallback()
 276{
 277  if ( !inputData_.usingCallback ) {
 278    errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";
 279    RtMidi::error( RtError::WARNING, errorString_ );
 280    return;
 281  }
 282
 283  inputData_.userCallback = 0;
 284  inputData_.userData = 0;
 285  inputData_.usingCallback = false;
 286}
 287
 288void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )
 289{
 290  inputData_.ignoreFlags = 0;
 291  if ( midiSysex ) inputData_.ignoreFlags = 0x01;
 292  if ( midiTime ) inputData_.ignoreFlags |= 0x02;
 293  if ( midiSense ) inputData_.ignoreFlags |= 0x04;
 294}
 295
 296double MidiInApi :: getMessage( std::vector<unsigned char> *message )
 297{
 298  message->clear();
 299
 300  if ( inputData_.usingCallback ) {
 301    errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";
 302    RtMidi::error( RtError::WARNING, errorString_ );
 303    return 0.0;
 304  }
 305
 306  if ( inputData_.queue.size == 0 ) return 0.0;
 307
 308  // Copy queued message to the vector pointer argument and then "pop" it.
 309  std::vector<unsigned char> *bytes = &(inputData_.queue.ring[inputData_.queue.front].bytes);
 310  message->assign( bytes->begin(), bytes->end() );
 311  double deltaTime = inputData_.queue.ring[inputData_.queue.front].timeStamp;
 312  inputData_.queue.size--;
 313  inputData_.queue.front++;
 314  if ( inputData_.queue.front == inputData_.queue.ringSize )
 315    inputData_.queue.front = 0;
 316
 317  return deltaTime;
 318}
 319
 320//*********************************************************************//
 321//  Common MidiOutApi Definitions
 322//*********************************************************************//
 323
 324MidiOutApi :: MidiOutApi( void )
 325  : apiData_( 0 ), connected_( false )
 326{
 327}
 328
 329MidiOutApi :: ~MidiOutApi( void )
 330{
 331}
 332
 333// *************************************************** //
 334//
 335// OS/API-specific methods.
 336//
 337// *************************************************** //
 338
 339#if defined(__MACOSX_CORE__)
 340
 341// The CoreMIDI API is based on the use of a callback function for
 342// MIDI input.  We convert the system specific time stamps to delta
 343// time values.
 344
 345// OS-X CoreMIDI header files.
 346#include <CoreMIDI/CoreMIDI.h>
 347#include <CoreAudio/HostTime.h>
 348#include <CoreServices/CoreServices.h>
 349
 350// A structure to hold variables related to the CoreMIDI API
 351// implementation.
 352struct CoreMidiData {
 353  MIDIClientRef client;
 354  MIDIPortRef port;
 355  MIDIEndpointRef endpoint;
 356  MIDIEndpointRef destinationId;
 357  unsigned long long lastTime;
 358  MIDISysexSendRequest sysexreq;
 359};
 360
 361//*********************************************************************//
 362//  API: OS-X
 363//  Class Definitions: MidiInCore
 364//*********************************************************************//
 365
 366void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef )
 367{
 368  MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef);
 369  CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
 370
 371  unsigned char status;
 372  unsigned short nBytes, iByte, size;
 373  unsigned long long time;
 374
 375  bool& continueSysex = data->continueSysex;
 376  MidiInApi::MidiMessage& message = data->message;
 377
 378  const MIDIPacket *packet = &list->packet[0];
 379  for ( unsigned int i=0; i<list->numPackets; ++i ) {
 380
 381    // My interpretation of the CoreMIDI documentation: all message
 382    // types, except sysex, are complete within a packet and there may
 383    // be several of them in a single packet.  Sysex messages can be
 384    // broken across multiple packets and PacketLists but are bundled
 385    // alone within each packet (these packets do not contain other
 386    // message types).  If sysex messages are split across multiple
 387    // MIDIPacketLists, they must be handled by multiple calls to this
 388    // function.
 389
 390    nBytes = packet->length;
 391    if ( nBytes == 0 ) continue;
 392
 393    // Calculate time stamp.
 394
 395    if ( data->firstMessage ) {
 396      message.timeStamp = 0.0;
 397      data->firstMessage = false;
 398    }
 399    else {
 400      time = packet->timeStamp;
 401      if ( time == 0 ) { // this happens when receiving asynchronous sysex messages
 402        time = AudioGetCurrentHostTime();
 403      }
 404      time -= apiData->lastTime;
 405      time = AudioConvertHostTimeToNanos( time );
 406      if ( !continueSysex )
 407        message.timeStamp = time * 0.000000001;
 408    }
 409    apiData->lastTime = packet->timeStamp;
 410    if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages
 411      apiData->lastTime = AudioGetCurrentHostTime();
 412    }
 413    //std::cout << "TimeStamp = " << packet->timeStamp << std::endl;
 414
 415    iByte = 0;
 416    if ( continueSysex ) {
 417      // We have a continuing, segmented sysex message.
 418      if ( !( data->ignoreFlags & 0x01 ) ) {
 419        // If we're not ignoring sysex messages, copy the entire packet.
 420        for ( unsigned int j=0; j<nBytes; ++j )
 421          message.bytes.push_back( packet->data[j] );
 422      }
 423      continueSysex = packet->data[nBytes-1] != 0xF7;
 424
 425      if ( !continueSysex ) {
 426        // If not a continuing sysex message, invoke the user callback function or queue the message.
 427        if ( data->usingCallback ) {
 428          RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
 429          callback( message.timeStamp, &message.bytes, data->userData );
 430        }
 431        else {
 432          // As long as we haven't reached our queue size limit, push the message.
 433          if ( data->queue.size < data->queue.ringSize ) {
 434            data->queue.ring[data->queue.back++] = message;
 435            if ( data->queue.back == data->queue.ringSize )
 436              data->queue.back = 0;
 437            data->queue.size++;
 438          }
 439          else
 440            std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
 441        }
 442        message.bytes.clear();
 443      }
 444    }
 445    else {
 446      while ( iByte < nBytes ) {
 447        size = 0;
 448        // We are expecting that the next byte in the packet is a status byte.
 449        status = packet->data[iByte];
 450        if ( !(status & 0x80) ) break;
 451        // Determine the number of bytes in the MIDI message.
 452        if ( status < 0xC0 ) size = 3;
 453        else if ( status < 0xE0 ) size = 2;
 454        else if ( status < 0xF0 ) size = 3;
 455        else if ( status == 0xF0 ) {
 456          // A MIDI sysex
 457          if ( data->ignoreFlags & 0x01 ) {
 458            size = 0;
 459            iByte = nBytes;
 460          }
 461          else size = nBytes - iByte;
 462          continueSysex = packet->data[nBytes-1] != 0xF7;
 463        }
 464        else if ( status == 0xF1 ) {
 465            // A MIDI time code message
 466           if ( data->ignoreFlags & 0x02 ) {
 467            size = 0;
 468            iByte += 2;
 469           }
 470           else size = 2;
 471        }
 472        else if ( status == 0xF2 ) size = 3;
 473        else if ( status == 0xF3 ) size = 2;
 474        else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {
 475          // A MIDI timing tick message and we're ignoring it.
 476          size = 0;
 477          iByte += 1;
 478        }
 479        else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {
 480          // A MIDI active sensing message and we're ignoring it.
 481          size = 0;
 482          iByte += 1;
 483        }
 484        else size = 1;
 485
 486        // Copy the MIDI data to our vector.
 487        if ( size ) {
 488          message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );
 489          if ( !continueSysex ) {
 490            // If not a continuing sysex message, invoke the user callback function or queue the message.
 491            if ( data->usingCallback ) {
 492              RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
 493              callback( message.timeStamp, &message.bytes, data->userData );
 494            }
 495            else {
 496              // As long as we haven't reached our queue size limit, push the message.
 497              if ( data->queue.size < data->queue.ringSize ) {
 498                data->queue.ring[data->queue.back++] = message;
 499                if ( data->queue.back == data->queue.ringSize )
 500                  data->queue.back = 0;
 501                data->queue.size++;
 502              }
 503              else
 504                std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
 505            }
 506            message.bytes.clear();
 507          }
 508          iByte += size;
 509        }
 510      }
 511    }
 512    packet = MIDIPacketNext(packet);
 513  }
 514}
 515
 516MidiInCore :: MidiInCore( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
 517{
 518  initialize( clientName );
 519}
 520
 521MidiInCore :: ~MidiInCore( void )
 522{
 523  // Close a connection if it exists.
 524  closePort();
 525
 526  // Cleanup.
 527  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 528  MIDIClientDispose( data->client );
 529  if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
 530  delete data;
 531}
 532
 533void MidiInCore :: initialize( const std::string& clientName )
 534{
 535  // Set up our client.
 536  MIDIClientRef client;
 537  OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
 538  if ( result != noErr ) {
 539    errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object.";
 540    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 541  }
 542
 543  // Save our api-specific connection information.
 544  CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
 545  data->client = client;
 546  data->endpoint = 0;
 547  apiData_ = (void *) data;
 548  inputData_.apiData = (void *) data;
 549}
 550
 551void MidiInCore :: openPort( unsigned int portNumber, const std::string portName )
 552{
 553  if ( connected_ ) {
 554    errorString_ = "MidiInCore::openPort: a valid connection already exists!";
 555    RtMidi::error( RtError::WARNING, errorString_ );
 556    return;
 557  }
 558
 559  unsigned int nSrc = MIDIGetNumberOfSources();
 560  if (nSrc < 1) {
 561    errorString_ = "MidiInCore::openPort: no MIDI input sources found!";
 562    RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ );
 563  }
 564
 565  std::ostringstream ost;
 566  if ( portNumber >= nSrc ) {
 567    ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
 568    errorString_ = ost.str();
 569    RtMidi::error( RtError::INVALID_PARAMETER, errorString_ );
 570  }
 571
 572  MIDIPortRef port;
 573  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 574  OSStatus result = MIDIInputPortCreate( data->client, 
 575                                         CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
 576                                         midiInputCallback, (void *)&inputData_, &port );
 577  if ( result != noErr ) {
 578    MIDIClientDispose( data->client );
 579    errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
 580    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 581  }
 582
 583  // Get the desired input source identifier.
 584  MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
 585  if ( endpoint == 0 ) {
 586    MIDIPortDispose( port );
 587    MIDIClientDispose( data->client );
 588    errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
 589    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 590  }
 591
 592  // Make the connection.
 593  result = MIDIPortConnectSource( port, endpoint, NULL );
 594  if ( result != noErr ) {
 595    MIDIPortDispose( port );
 596    MIDIClientDispose( data->client );
 597    errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
 598    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 599  }
 600
 601  // Save our api-specific port information.
 602  data->port = port;
 603
 604  connected_ = true;
 605}
 606
 607void MidiInCore :: openVirtualPort( const std::string portName )
 608{
 609  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 610
 611  // Create a virtual MIDI input destination.
 612  MIDIEndpointRef endpoint;
 613  OSStatus result = MIDIDestinationCreate( data->client,
 614                                           CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
 615                                           midiInputCallback, (void *)&inputData_, &endpoint );
 616  if ( result != noErr ) {
 617    errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination.";
 618    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 619  }
 620
 621  // Save our api-specific connection information.
 622  data->endpoint = endpoint;
 623}
 624
 625void MidiInCore :: closePort( void )
 626{
 627  if ( connected_ ) {
 628    CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 629    MIDIPortDispose( data->port );
 630    connected_ = false;
 631  }
 632}
 633
 634unsigned int MidiInCore :: getPortCount()
 635{
 636  return MIDIGetNumberOfSources();
 637}
 638
 639// This function was submitted by Douglas Casey Tucker and apparently
 640// derived largely from PortMidi.
 641CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
 642{
 643  CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
 644  CFStringRef str;
 645
 646  // Begin with the endpoint's name.
 647  str = NULL;
 648  MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );
 649  if ( str != NULL ) {
 650    CFStringAppend( result, str );
 651    CFRelease( str );
 652  }
 653
 654  MIDIEntityRef entity = NULL;
 655  MIDIEndpointGetEntity( endpoint, &entity );
 656  if ( entity == 0 )
 657    // probably virtual
 658    return result;
 659
 660  if ( CFStringGetLength( result ) == 0 ) {
 661    // endpoint name has zero length -- try the entity
 662    str = NULL;
 663    MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );
 664    if ( str != NULL ) {
 665      CFStringAppend( result, str );
 666      CFRelease( str );
 667    }
 668  }
 669  // now consider the device's name
 670  MIDIDeviceRef device = 0;
 671  MIDIEntityGetDevice( entity, &device );
 672  if ( device == 0 )
 673    return result;
 674
 675  str = NULL;
 676  MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );
 677  if ( CFStringGetLength( result ) == 0 ) {
 678      CFRelease( result );
 679      return str;
 680  }
 681  if ( str != NULL ) {
 682    // if an external device has only one entity, throw away
 683    // the endpoint name and just use the device name
 684    if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {
 685      CFRelease( result );
 686      return str;
 687    } else {
 688      if ( CFStringGetLength( str ) == 0 ) {
 689        CFRelease( str );
 690        return result;
 691      }
 692      // does the entity name already start with the device name?
 693      // (some drivers do this though they shouldn't)
 694      // if so, do not prepend
 695        if ( CFStringCompareWithOptions( result, /* endpoint name */
 696             str /* device name */,
 697             CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {
 698        // prepend the device name to the entity name
 699        if ( CFStringGetLength( result ) > 0 )
 700          CFStringInsert( result, 0, CFSTR(" ") );
 701        CFStringInsert( result, 0, str );
 702      }
 703      CFRelease( str );
 704    }
 705  }
 706  return result;
 707}
 708
 709// This function was submitted by Douglas Casey Tucker and apparently
 710// derived largely from PortMidi.
 711static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )
 712{
 713  CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
 714  CFStringRef str;
 715  OSStatus err;
 716  int i;
 717
 718  // Does the endpoint have connections?
 719  CFDataRef connections = NULL;
 720  int nConnected = 0;
 721  bool anyStrings = false;
 722  err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );
 723  if ( connections != NULL ) {
 724    // It has connections, follow them
 725    // Concatenate the names of all connected devices
 726    nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);
 727    if ( nConnected ) {
 728      const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
 729      for ( i=0; i<nConnected; ++i, ++pid ) {
 730        MIDIUniqueID id = EndianS32_BtoN( *pid );
 731        MIDIObjectRef connObject;
 732        MIDIObjectType connObjectType;
 733        err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );
 734        if ( err == noErr ) {
 735          if ( connObjectType == kMIDIObjectType_ExternalSource  ||
 736              connObjectType == kMIDIObjectType_ExternalDestination ) {
 737            // Connected to an external device's endpoint (10.3 and later).
 738            str = EndpointName( (MIDIEndpointRef)(connObject), true );
 739          } else {
 740            // Connected to an external device (10.2) (or something else, catch-
 741            str = NULL;
 742            MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );
 743          }
 744          if ( str != NULL ) {
 745            if ( anyStrings )
 746              CFStringAppend( result, CFSTR(", ") );
 747            else anyStrings = true;
 748            CFStringAppend( result, str );
 749            CFRelease( str );
 750          }
 751        }
 752      }
 753    }
 754    CFRelease( connections );
 755  }
 756  if ( anyStrings )
 757    return result;
 758
 759  // Here, either the endpoint had no connections, or we failed to obtain names 
 760  return EndpointName( endpoint, false );
 761}
 762
 763std::string MidiInCore :: getPortName( unsigned int portNumber )
 764{
 765  CFStringRef nameRef;
 766  MIDIEndpointRef portRef;
 767  std::ostringstream ost;
 768  char name[128];
 769
 770  std::string stringName;
 771  if ( portNumber >= MIDIGetNumberOfSources() ) {
 772    ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
 773    errorString_ = ost.str();
 774    RtMidi::error( RtError::WARNING, errorString_ );
 775    //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ );
 776    return stringName;
 777  }
 778
 779  portRef = MIDIGetSource( portNumber );
 780  nameRef = ConnectedEndpointName(portRef);
 781  CFStringGetCString( nameRef, name, sizeof(name), 0);
 782  CFRelease( nameRef );
 783
 784  return stringName = name;
 785}
 786
 787//*********************************************************************//
 788//  API: OS-X
 789//  Class Definitions: MidiOutCore
 790//*********************************************************************//
 791
 792MidiOutCore :: MidiOutCore( const std::string clientName ) : MidiOutApi()
 793{
 794  initialize( clientName );
 795}
 796
 797MidiOutCore :: ~MidiOutCore( void )
 798{
 799  // Close a connection if it exists.
 800  closePort();
 801
 802  // Cleanup.
 803  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 804  MIDIClientDispose( data->client );
 805  if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
 806  delete data;
 807}
 808
 809void MidiOutCore :: initialize( const std::string& clientName )
 810{
 811  // Set up our client.
 812  MIDIClientRef client;
 813  OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
 814  if ( result != noErr ) {
 815    errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object.";
 816    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 817  }
 818
 819  // Save our api-specific connection information.
 820  CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
 821  data->client = client;
 822  data->endpoint = 0;
 823  apiData_ = (void *) data;
 824}
 825
 826unsigned int MidiOutCore :: getPortCount()
 827{
 828  return MIDIGetNumberOfDestinations();
 829}
 830
 831std::string MidiOutCore :: getPortName( unsigned int portNumber )
 832{
 833  CFStringRef nameRef;
 834  MIDIEndpointRef portRef;
 835  std::ostringstream ost;
 836  char name[128];
 837
 838  std::string stringName;
 839  if ( portNumber >= MIDIGetNumberOfDestinations() ) {
 840    ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
 841    errorString_ = ost.str();
 842    RtMidi::error( RtError::WARNING, errorString_ );
 843    return stringName;
 844    //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ );
 845  }
 846
 847  portRef = MIDIGetDestination( portNumber );
 848  nameRef = ConnectedEndpointName(portRef);
 849  CFStringGetCString( nameRef, name, sizeof(name), 0);
 850  CFRelease( nameRef );
 851  
 852  return stringName = name;
 853}
 854
 855void MidiOutCore :: openPort( unsigned int portNumber, const std::string portName )
 856{
 857  if ( connected_ ) {
 858    errorString_ = "MidiOutCore::openPort: a valid connection already exists!";
 859    RtMidi::error( RtError::WARNING, errorString_ );
 860    return;
 861  }
 862
 863  unsigned int nDest = MIDIGetNumberOfDestinations();
 864  if (nDest < 1) {
 865    errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!";
 866    RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ );
 867  }
 868
 869  std::ostringstream ost;
 870  if ( portNumber >= nDest ) {
 871    ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
 872    errorString_ = ost.str();
 873    RtMidi::error( RtError::INVALID_PARAMETER, errorString_ );
 874  }
 875
 876  MIDIPortRef port;
 877  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 878  OSStatus result = MIDIOutputPortCreate( data->client, 
 879                                          CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
 880                                          &port );
 881  if ( result != noErr ) {
 882    MIDIClientDispose( data->client );
 883    errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
 884    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 885  }
 886
 887  // Get the desired output port identifier.
 888  MIDIEndpointRef destination = MIDIGetDestination( portNumber );
 889  if ( destination == 0 ) {
 890    MIDIPortDispose( port );
 891    MIDIClientDispose( data->client );
 892    errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
 893    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 894  }
 895
 896  // Save our api-specific connection information.
 897  data->port = port;
 898  data->destinationId = destination;
 899  connected_ = true;
 900}
 901
 902void MidiOutCore :: closePort( void )
 903{
 904  if ( connected_ ) {
 905    CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 906    MIDIPortDispose( data->port );
 907    connected_ = false;
 908  }
 909}
 910
 911void MidiOutCore :: openVirtualPort( std::string portName )
 912{
 913  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 914
 915  if ( data->endpoint ) {
 916    errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!";
 917    RtMidi::error( RtError::WARNING, errorString_ );
 918    return;
 919  }
 920
 921  // Create a virtual MIDI output source.
 922  MIDIEndpointRef endpoint;
 923  OSStatus result = MIDISourceCreate( data->client,
 924                                      CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
 925                                      &endpoint );
 926  if ( result != noErr ) {
 927    errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source.";
 928    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
 929  }
 930
 931  // Save our api-specific connection information.
 932  data->endpoint = endpoint;
 933}
 934
 935char *sysexBuffer = 0;
 936
 937void sysexCompletionProc( MIDISysexSendRequest * sreq )
 938{
 939  //std::cout << "Completed SysEx send\n";
 940 delete sysexBuffer;
 941 sysexBuffer = 0;
 942}
 943
 944void MidiOutCore :: sendMessage( std::vector<unsigned char> *message )
 945{
 946  // We use the MIDISendSysex() function to asynchronously send sysex
 947  // messages.  Otherwise, we use a single CoreMidi MIDIPacket.
 948  unsigned int nBytes = message->size();
 949  if ( nBytes == 0 ) {
 950    errorString_ = "MidiOutCore::sendMessage: no data in message argument!";      
 951    RtMidi::error( RtError::WARNING, errorString_ );
 952    return;
 953  }
 954
 955  //  unsigned int packetBytes, bytesLeft = nBytes;
 956  //  unsigned int messageIndex = 0;
 957  MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
 958  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 959  OSStatus result;
 960
 961  if ( message->at(0) == 0xF0 ) {
 962
 963    while ( sysexBuffer != 0 ) usleep( 1000 ); // sleep 1 ms
 964
 965   sysexBuffer = new char[nBytes];
 966   if ( sysexBuffer == NULL ) {
 967     errorString_ = "MidiOutCore::sendMessage: error allocating sysex message memory!";
 968     RtMidi::error( RtError::MEMORY_ERROR, errorString_ );
 969   }
 970
 971   // Copy data to buffer.
 972   for ( unsigned int i=0; i<nBytes; ++i ) sysexBuffer[i] = message->at(i);
 973
 974   data->sysexreq.destination = data->destinationId;
 975   data->sysexreq.data = (Byte *)sysexBuffer;
 976   data->sysexreq.bytesToSend = nBytes;
 977   data->sysexreq.complete = 0;
 978   data->sysexreq.completionProc = sysexCompletionProc;
 979   data->sysexreq.completionRefCon = &(data->sysexreq);
 980
 981   result = MIDISendSysex( &(data->sysexreq) );
 982   if ( result != noErr ) {
 983     errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
 984     RtMidi::error( RtError::WARNING, errorString_ );
 985   }
 986   return;
 987  }
 988  else if ( nBytes > 3 ) {
 989   errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
 990   RtMidi::error( RtError::WARNING, errorString_ );
 991   return;
 992  }
 993
 994  MIDIPacketList packetList;
 995  MIDIPacket *packet = MIDIPacketListInit( &packetList );
 996  packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) );
 997  if ( !packet ) {
 998    errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";      
 999    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
1000  }
1001
1002  // Send to any destinations that may have connected to us.
1003  if ( data->endpoint ) {
1004    result = MIDIReceived( data->endpoint, &packetList );
1005    if ( result != noErr ) {
1006      errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
1007      RtMidi::error( RtError::WARNING, errorString_ );
1008    }
1009  }
1010
1011  // And send to an explicit destination port if we're connected.
1012  if ( connected_ ) {
1013    result = MIDISend( data->port, data->destinationId, &packetList );
1014    if ( result != noErr ) {
1015      errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
1016      RtMidi::error( RtError::WARNING, errorString_ );
1017    }
1018  }
1019
1020}
1021
1022#endif  // __MACOSX_CORE__
1023
1024
1025//*********************************************************************//
1026//  API: LINUX ALSA SEQUENCER
1027//*********************************************************************//
1028
1029// API information found at:
1030//   - http://www.alsa-project.org/documentation.php#Library
1031
1032#if defined(__LINUX_ALSA__)
1033
1034// The ALSA Sequencer API is based on the use of a callback function for
1035// MIDI input.
1036//
1037// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
1038// time stamps and other assorted fixes!!!
1039
1040// If you don't need timestamping for incoming MIDI events, define the
1041// preprocessor definition AVOID_TIMESTAMPING to save resources
1042// associated with the ALSA sequencer queues.
1043
1044#include <pthread.h>
1045#include <sys/time.h>
1046
1047// ALSA header file.
1048#include <alsa/asoundlib.h>
1049
1050// Global sequencer instance created when first In/Out object is
1051// created, then destroyed when last In/Out is deleted.
1052static snd_seq_t *s_seq = NULL;
1053
1054// Variable to keep track of how many ports are open.
1055static unsigned int s_numPorts = 0;
1056
1057// The client name to use when creating the sequencer, which is
1058// currently set on the first call to createSequencer.
1059static std::string s_clientName = "RtMidi Client";
1060
1061// A structure to hold variables related to the ALSA API
1062// implementation.
1063struct AlsaMidiData {
1064  snd_seq_t *seq;
1065  unsigned int portNum;
1066  int vport;
1067  snd_seq_port_subscribe_t *subscription;
1068  snd_midi_event_t *coder;
1069  unsigned int bufferSize;
1070  unsigned char *buffer;
1071  pthread_t thread;
1072  pthread_t dummy_thread_id;
1073  unsigned long long lastTime;
1074  int queue_id; // an input queue is needed to get timestamped events
1075  int trigger_fds[2];
1076};
1077
1078#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
1079
1080snd_seq_t* createSequencer( const std::string& clientName )
1081{
1082  // Set up the ALSA sequencer client.
1083  if ( s_seq == NULL ) {
1084    int result = snd_seq_open(&s_seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
1085    if ( result < 0 ) {
1086      s_seq = NULL;
1087    }
1088    else {
1089      // Set client name, use current name if given string is empty.
1090      if ( clientName != "" ) {
1091        s_clientName = clientName;
1092      }
1093      snd_seq_set_client_name( s_seq, s_clientName.c_str() );
1094    }
1095  }
1096
1097  // Increment port count.
1098  s_numPorts++;
1099
1100  return s_seq;
1101}
1102
1103void freeSequencer ( void )
1104{
1105  s_numPorts--;
1106  if ( s_numPorts == 0 && s_seq != NULL ) {
1107    snd_seq_close( s_seq );
1108    s_seq = NULL;
1109  }
1110}
1111
1112//*********************************************************************//
1113//  API: LINUX ALSA
1114//  Class Definitions: MidiInAlsa
1115//*********************************************************************//
1116
1117extern "C" void *alsaMidiHandler( void *ptr )
1118{
1119  MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr);
1120  AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);
1121
1122  long nBytes;
1123  unsigned long long time, lastTime;
1124  bool continueSysex = false;
1125  bool doDecode = false;
1126  MidiInApi::MidiMessage message;
1127  int poll_fd_count;
1128  struct pollfd *poll_fds;
1129
1130  snd_seq_event_t *ev;
1131  int result;
1132  apiData->bufferSize = 32;
1133  result = snd_midi_event_new( 0, &apiData->coder );
1134  if ( result < 0 ) {
1135    data->doInput = false;
1136    std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n";
1137    return 0;
1138  }
1139  unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );
1140  if ( buffer == NULL ) {
1141    data->doInput = false;
1142    snd_midi_event_free( apiData->coder );
1143    apiData->coder = 0;
1144    std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n";
1145    return 0;
1146  }
1147  snd_midi_event_init( apiData->coder );
1148  snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages
1149
1150  poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1;
1151  poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd ));
1152  snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN );
1153  poll_fds[0].fd = apiData->trigger_fds[0];
1154  poll_fds[0].events = POLLIN;
1155
1156  while ( data->doInput ) {
1157
1158    if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {
1159      // No data pending
1160      if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) {
1161        if ( poll_fds[0].revents & POLLIN ) {
1162          bool dummy;
1163          int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) );
1164          (void) res;
1165        }
1166      }
1167      continue;
1168    }
1169
1170    // If here, there should be data.
1171    result = snd_seq_event_input( apiData->seq, &ev );
1172    if ( result == -ENOSPC ) {
1173      std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n";
1174      continue;
1175    }
1176    else if ( result <= 0 ) {
1177      std::cerr << "MidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n";
1178      continue;
1179    }
1180
1181    // This is a bit weird, but we now have to decode an ALSA MIDI
1182    // event (back) into MIDI bytes.  We'll ignore non-MIDI types.
1183    if ( !continueSysex ) message.bytes.clear();
1184
1185    doDecode = false;
1186    switch ( ev->type ) {
1187
1188		case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1189#if defined(__RTMIDI_DEBUG__)
1190      std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n";
1191#endif
1192      break;
1193
1194		case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
1195#if defined(__RTMIDI_DEBUG__)
1196      std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n";
1197      std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"
1198                << (int) ev->data.connect.sender.port
1199                << ", dest = " << (int) ev->data.connect.dest.client << ":"
1200                << (int) ev->data.connect.dest.port
1201                << std::endl;
1202#endif
1203      break;
1204
1205    case SND_SEQ_EVENT_QFRAME: // MIDI time code
1206      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
1207      break;
1208
1209    case SND_SEQ_EVENT_TICK: // MIDI timing tick
1210      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
1211      break;
1212
1213    case SND_SEQ_EVENT_SENSING: // Active sensing
1214      if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;
1215      break;
1216
1217		case SND_SEQ_EVENT_SYSEX:
1218      if ( (data->ignoreFlags & 0x01) ) break;
1219      if ( ev->data.ext.len > apiData->bufferSize ) {
1220        apiData->bufferSize = ev->data.ext.len;
1221        free( buffer );
1222        buffer = (unsigned char *) malloc( apiData->bufferSize );
1223        if ( buffer == NULL ) {
1224          data->doInput = false;
1225          std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n";
1226          break;
1227        }
1228      }
1229
1230    default:
1231      doDecode = true;
1232    }
1233
1234    if ( doDecode ) {
1235
1236      nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
1237      if ( nBytes > 0 ) {
1238        // The ALSA sequencer has a maximum buffer size for MIDI sysex
1239        // events of 256 bytes.  If a device sends sysex messages larger
1240        // than this, they are segmented into 256 byte chunks.  So,
1241        // we'll watch for this and concatenate sysex chunks into a
1242        // single sysex message if necessary.
1243        if ( !continueSysex )
1244          message.bytes.assign( buffer, &buffer[nBytes] );
1245        else
1246          message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
1247
1248        continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
1249        if ( !continueSysex ) {
1250
1251          // Calculate the time stamp:
1252          message.timeStamp = 0.0;
1253
1254          // Method 1: Use the system time.
1255          //(void)gettimeofday(&tv, (struct timezone *)NULL);
1256          //time = (tv.tv_sec * 1000000) + tv.tv_usec;
1257
1258          // Method 2: Use the ALSA sequencer event time data.
1259          // (thanks to Pedro Lopez-Cabanillas!).
1260          time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
1261          lastTime = time;
1262          time -= apiData->lastTime;
1263          apiData->lastTime = lastTime;
1264          if ( data->firstMessage == true )
1265            data->firstMessage = false;
1266          else
1267            message.timeStamp = time * 0.000001;
1268        }
1269        else {
1270#if defined(__RTMIDI_DEBUG__)
1271          std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
1272#endif
1273        }
1274      }
1275    }
1276
1277    snd_seq_free_event( ev );
1278    if ( message.bytes.size() == 0 || continueSysex ) continue;
1279
1280    if ( data->usingCallback ) {
1281      RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
1282      callback( message.timeStamp, &message.bytes, data->userData );
1283    }
1284    else {
1285      // As long as we haven't reached our queue size limit, push the message.
1286      if ( data->queue.size < data->queue.ringSize ) {
1287        data->queue.ring[data->queue.back++] = message;
1288        if ( data->queue.back == data->queue.ringSize )
1289          data->queue.back = 0;
1290        data->queue.size++;
1291      }
1292      else
1293        std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n";
1294    }
1295  }
1296
1297  if ( buffer ) free( buffer );
1298  snd_midi_event_free( apiData->coder );
1299  apiData->coder = 0;
1300  apiData->thread = apiData->dummy_thread_id;
1301  return 0;
1302}
1303
1304MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
1305{
1306  initialize( clientName );
1307}
1308
1309MidiInAlsa :: ~MidiInAlsa()
1310{
1311  // Close a connection if it exists.
1312  closePort();
1313
1314  // Shutdown the input thread.
1315  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1316  if ( inputData_.doInput ) {
1317    inputData_.doInput = false;
1318    int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );
1319    (void) res;
1320    if ( !pthread_equal(data->thread, data->dummy_thread_id) )
1321      pthread_join( data->thread, NULL );
1322  }
1323
1324  // Cleanup.
1325  close ( data->trigger_fds[0] );
1326  close ( data->trigger_fds[1] );
1327  if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
1328#ifndef AVOID_TIMESTAMPING
1329  snd_seq_free_queue( data->seq, data->queue_id );
1330#endif
1331  freeSequencer();
1332  delete data;
1333}
1334
1335void MidiInAlsa :: initialize( const std::string& clientName )
1336{
1337  snd_seq_t* seq = createSequencer( clientName );
1338  if ( seq == NULL ) {
1339    s_seq = NULL;
1340    errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object.";
1341    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
1342  }
1343
1344  // Save our api-specific connection information.
1345  AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
1346  data->seq = seq;
1347  data->portNum = -1;
1348  data->vport = -1;
1349  data->subscription = 0;
1350  data->dummy_thread_id = pthread_self();
1351  data->thread = data->dummy_thread_id;
1352  data->trigger_fds[0] = -1;
1353  data->trigger_fds[1] = -1;
1354  apiData_ = (void *) data;
1355  inputData_.apiData = (void *) data;
1356
1357   if ( pipe(data->trigger_fds) == -1 ) {
1358    errorString_ = "MidiInAlsa::initialize: error creating pipe objects.";
1359    RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
1360  }
1361
1362  // Create the input queue
1363#ifndef AVOID_TIMESTAMPING
1364  data->queue_id = snd_seq_alloc_named_queue(s_seq, "RtMidi Queue");
1365  // Set arbitrary tempo (mm=100) and resolution (240)
1366  snd_seq_queue_tempo_t *qtempo;
1367  snd_seq_queue_tempo_alloca(&qtempo);
1368  snd_seq_queue_tempo_set_tempo(qtempo, 600000);
1369  snd_seq_queue_tempo_set_ppq(qtempo, 240);
1370  snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo);
1371  snd_seq_drain_output(data->seq);
1372#endif
1373}
1374
1375// This function is used to count or get the pinfo structure for a given port number.
1376unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )
1377{
1378  snd_seq_client_info_t *cinfo;
1379  int client;
1380  int count = 0;
1381  snd_seq_client_info_alloca( &cinfo );
1382
1383  snd_seq_client_info_set_client( cinfo, -1 );
1384  while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {
1385    client = snd_seq_client_info_get_client( cinfo );
1386    if ( client == 0 ) continue;
1387    // Reset query info
1388    snd_seq_port_info_set_client( pinfo, client );
1389    snd_seq_port_info_set_port( pinfo, -1 );
1390    while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
1391      unsigned int atyp = snd_seq_port_info_get_type( pinfo );
1392      if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue;
1393      unsigned int caps = snd_seq_port_info_get_capability( pinfo );
1394      if ( ( caps & type ) != type ) continue;
1395      if ( count == portNumber ) return 1;
1396      ++count;
1397    }
1398  }
1399
1400  // If a negative portNumber was used, return the port count.
1401  if ( portNumber < 0 ) return count;
1402  return 0;
1403}
1404
1405unsigned int MidiInAlsa :: getPortCount()
1406{
1407  snd_seq_port_info_t *pinfo;
1408  snd_seq_port_info_alloca( &pinfo );
1409
1410  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1411  return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );
1412}
1413
1414std::string MidiInAlsa :: getPortName( unsigned int portNumber )
1415{
1416  snd_seq_client_info_t *cinfo;
1417  snd_seq_port_info_t *pinfo;
1418  snd_seq_client_info_alloca( &cinfo );
1419  snd_seq_port_info_alloca( &pinfo );
1420
1421  std::string stringName;
1422  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1423  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
1424    int cnum = snd_seq_port_info_get_client( pinfo );
1425    snd_seq_get_any_client_info( data->seq, cnum, cinfo );
1426    std::ostringstream os;
1427    os << snd_seq_client_info_get_name( cinfo );
1428    os << " ";                                    // GO: These lines added to make sure devices are listed
1429    os << snd_seq_port_info_get_client( pinfo );  // GO: with full portnames added to ensure individual device names
1430    os << ":";
1431    os << snd_seq_port_info_get_port( pinfo );
1432    stringName = os.str();
1433    return stringName;
1434  }
1435
1436  // If we get here, we didn't find a match.
1437  errorString_ = "MidiInAlsa::getPortName: error looking for port name!";
1438  RtMidi::error( RtError::WARNING, errorString_ );
1439  return stringName;
1440  //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ );
1441}
1442
1443void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName )
1444{
1445  if ( connected_ ) {
1446    errorString_ = "MidiInAlsa::openPort: a valid connection already exists!";
1447    RtMidi::error( RtError::WARNING, errorString_ );
1448    return;
1449  }
1450
1451  unsigned int nSrc = this->getPortCount();
1452  if (nSrc < 1) {
1453    errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!";
1454    RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ );
1455  }
1456
1457  snd_seq_port_info_t *pinfo;
1458  snd_seq_port_info_alloca( &pinfo );
1459  std::ostringstream ost;
1460  AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1461  if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {
1462    ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
1463    errorString_ = ost.str();
1464    RtMidi::error( RtError::INVALID_PARAMETER, errorString_ );
1465  }
1466
1467
1468  snd_seq_addr_t sender, receiver;
1469  sender.client = snd_seq_port_info_get_client( pinfo );
1470  sender.port = snd_seq_port_info_get_port( pinfo );
1471  receiver.client = snd_seq_client_id( data->seq );
1472  if ( data->vport < 0 ) {
1473    snd_seq_port_info_set_client( pinfo, 0 );
1474    snd_seq_port_info_set_port( pinfo, 0 );
1475    snd_seq_port_info_set_capability( pinfo,
1476                                      SND_SEQ_PORT_CAP_WRITE |
1477                                      SND_SEQ_PORT_CAP_SUBS_WRITE );
1478    snd_seq_port_info_set_type( pinfo,
1479                                SND_SEQ_PORT_TYPE_MIDI_GENERIC |
1480                                SND_SEQ_PORT_TYPE_APPLICATION );
1481    snd_seq_port_info_set_midi_channels(pinfo, 16);
1482#ifndef AVOID_TIMESTAMPING
1483    snd_seq_port_info_set_timestamping(pinfo, 1);
1484    snd_seq_port_info_set_timestamp_real(pinfo, 1);    
1485    snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
1486#endif
1487    snd_seq_port_info_set_name(pinfo,  portName.c_str() );
1488    data->vport = snd_seq_create_port(data->seq, pinfo);
1489  
1490    if ( data->vport < 0 ) {
1491      errorString_ = "MidiInAlsa::openPort: ALSA error creating input port.";
1492      RtMidi::error( RtError::DRIVER_ERROR, errorString_ );
1493    }
1494  }
1495
1496  receiver.port = data->vport;

Large files files are truncated, but you can click here to view the full file