Browse Source

Version 1.0.12

Gary Scavone 11 năm trước cách đây
mục cha
commit
8fec3d71d4
14 tập tin đã thay đổi với 616 bổ sung262 xóa
  1. 198 138
      RtMidi.cpp
  2. 4 4
      RtMidi.h
  3. 91 32
      doc/doxygen/Doxyfile
  4. 1 1
      doc/doxygen/footer.html
  5. 8 7
      doc/doxygen/tutorial.txt
  6. 8 1
      doc/release.txt
  7. 1 0
      msw/readme
  8. 20 0
      msw/rtmidilib.sln
  9. 178 0
      msw/rtmidilib.vcproj
  10. 2 2
      readme
  11. 6 3
      tests/Makefile.in
  12. 61 32
      tests/cmidiin.cpp
  13. 17 1
      tests/midiout.cpp
  14. 21 41
      tests/midiprobe.cpp

+ 198 - 138
RtMidi.cpp

@@ -8,7 +8,7 @@
     RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
 
     RtMidi: realtime MIDI i/o C++ classes
-    Copyright (c) 2003-2010 Gary P. Scavone
+    Copyright (c) 2003-2011 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -35,7 +35,7 @@
 */
 /**********************************************************************/
 
-// RtMidi: Version 1.0.11
+// RtMidi: Version 1.0.12
 
 #include "RtMidi.h"
 #include <sstream>
@@ -176,6 +176,7 @@ struct CoreMidiData {
   MIDIEndpointRef endpoint;
   MIDIEndpointRef destinationId;
   unsigned long long lastTime;
+  MIDISysexSendRequest sysexreq;
 };
 
 //*********************************************************************//
@@ -216,11 +217,18 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
       data->firstMessage = false;
     else {
       time = packet->timeStamp;
+      if ( time == 0 ) { // this happens when receiving asynchronous sysex messages
+        time = AudioGetCurrentHostTime();
+      }
       time -= apiData->lastTime;
       time = AudioConvertHostTimeToNanos( time );
       message.timeStamp = time * 0.000000001;
     }
     apiData->lastTime = packet->timeStamp;
+    if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages
+      apiData->lastTime = AudioGetCurrentHostTime();
+    }
+    //std::cout << "TimeStamp = " << packet->timeStamp << std::endl;
 
     iByte = 0;
     if ( continueSysex ) {
@@ -265,26 +273,24 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
             iByte = nBytes;
           }
           else size = nBytes - iByte;
-          continueSysex =  packet->data[nBytes-1] != 0xF7;
+          continueSysex = packet->data[nBytes-1] != 0xF7;
         }
-        else if ( status < 0xF3 ) {
-          if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) {
-            // A MIDI time code message and we're ignoring it.
+        else if ( status == 0xF1 ) {
+            // A MIDI time code message
+           if ( data->ignoreFlags & 0x02 ) {
             size = 0;
-            iByte += 3;
-          }
-          else size = 3;
+            iByte += 2;
+           }
+           else size = 2;
         }
+        else if ( status == 0xF2 ) size = 3;
         else if ( status == 0xF3 ) size = 2;
-        else if ( status == 0xF8 ) {
-          size = 1;
-          if ( data->ignoreFlags & 0x02 ) {
-            // A MIDI timing tick message and we're ignoring it.
-            size = 0;
-            iByte += 3;
-          }
+        else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {
+          // A MIDI timing tick message and we're ignoring it.
+          size = 0;
+          iByte += 1;
         }
-        else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
+        else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {
           // A MIDI active sensing message and we're ignoring it.
           size = 0;
           iByte += 1;
@@ -566,21 +572,21 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber )
   std::ostringstream ost;
   char name[128];
 
+  std::string stringName;
   if ( portNumber >= MIDIGetNumberOfSources() ) {
     ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
     errorString_ = ost.str();
-    error( RtError::INVALID_PARAMETER );
+    error( RtError::WARNING );
+    //error( RtError::INVALID_PARAMETER );
+    return stringName;
   }
-  portRef = MIDIGetSource( portNumber );
 
+  portRef = MIDIGetSource( portNumber );
   nameRef = ConnectedEndpointName(portRef);
-  //MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
-  // modified by D. Casey Tucker 2009-03-10
-
   CFStringGetCString( nameRef, name, sizeof(name), 0);
   CFRelease( nameRef );
-  std::string stringName = name;
-  return stringName;
+
+  return stringName = name;
 }
 
 //*********************************************************************//
@@ -600,19 +606,21 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
   std::ostringstream ost;
   char name[128];
 
+  std::string stringName;
   if ( portNumber >= MIDIGetNumberOfDestinations() ) {
     ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
     errorString_ = ost.str();
-    error( RtError::INVALID_PARAMETER );
+    error( RtError::WARNING );
+    return stringName;
+    //error( RtError::INVALID_PARAMETER );
   }
-  portRef = MIDIGetDestination( portNumber );
 
+  portRef = MIDIGetDestination( portNumber );
   nameRef = ConnectedEndpointName(portRef);
-  //MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
   CFStringGetCString( nameRef, name, sizeof(name), 0);
   CFRelease( nameRef );
-  std::string stringName = name;
-  return stringName;
+  
+  return stringName = name;
 }
 
 void RtMidiOut :: initialize( const std::string& clientName )
@@ -724,11 +732,19 @@ RtMidiOut :: ~RtMidiOut()
   delete data;
 }
 
+char *sysexBuffer = 0;
+
+void sysexCompletionProc( MIDISysexSendRequest * sreq )
+{
+  //std::cout << "Completed SysEx send\n";
+ delete sysexBuffer;
+ sysexBuffer = 0;
+}
+
 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
 {
-  // The CoreMidi documentation indicates a maximum PackList size of
-  // 64K, so we may need to break long sysex messages into pieces and
-  // send via separate lists.
+  // We use the MIDISendSysex() function to asynchronously send sysex
+  // messages.  Otherwise, we use a single CoreMidi MIDIPacket.
   unsigned int nBytes = message->size();
   if ( nBytes == 0 ) {
     errorString_ = "RtMidiOut::sendMessage: no data in message argument!";      
@@ -736,51 +752,71 @@ void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
     return;
   }
 
-  if ( nBytes > 3 && ( message->at(0) != 0xF0 ) ) {
-    errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?";      
-    error( RtError::WARNING );
-    return;
-  }
-
-  unsigned int packetBytes, bytesLeft = nBytes;
-  unsigned int messageIndex = 0;
+  //  unsigned int packetBytes, bytesLeft = nBytes;
+  //  unsigned int messageIndex = 0;
   MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
+  OSStatus result;
 
-  while ( bytesLeft > 0 ) {
+  if ( message->at(0) == 0xF0 ) {
 
-    packetBytes = ( bytesLeft > 32736 ) ? 32736 : bytesLeft;
-    Byte buffer[packetBytes + 32]; // extra memory for other structure variables
-    MIDIPacketList *packetList = (MIDIPacketList *) buffer;
-    MIDIPacket *curPacket = MIDIPacketListInit( packetList );
+    while ( sysexBuffer != 0 ) usleep( 1000 ); // sleep 1 ms
+
+   sysexBuffer = new char[nBytes];
+   if ( sysexBuffer == NULL ) {
+     errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!";
+     error( RtError::MEMORY_ERROR );
+   }
+
+   // Copy data to buffer.
+   for ( unsigned int i=0; i<nBytes; ++i ) sysexBuffer[i] = message->at(i);
+
+   data->sysexreq.destination = data->destinationId;
+   data->sysexreq.data = (Byte *)sysexBuffer;
+   data->sysexreq.bytesToSend = nBytes;
+   data->sysexreq.complete = 0;
+   data->sysexreq.completionProc = sysexCompletionProc;
+   data->sysexreq.completionRefCon = &(data->sysexreq);
+
+   result = MIDISendSysex( &(data->sysexreq) );
+   if ( result != noErr ) {
+     errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
+     error( RtError::WARNING );
+   }
+   return;
+  }
+  else if ( nBytes > 3 ) {
+   errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?";
+   error( RtError::WARNING );
+   return;
+  }
+
+  MIDIPacketList packetList;
+  MIDIPacket *packet = MIDIPacketListInit( &packetList );
+  packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) );
+  if ( !packet ) {
+    errorString_ = "RtMidiOut::sendMessage: could not allocate packet list";      
+    error( RtError::DRIVER_ERROR );
+  }
 
-    curPacket = MIDIPacketListAdd( packetList, packetBytes+32, curPacket, timeStamp, packetBytes, (const Byte *) &message->at( messageIndex ) );
-    if ( !curPacket ) {
-      errorString_ = "RtMidiOut::sendMessage: could not allocate packet list";      
-      error( RtError::DRIVER_ERROR );
-    }
-    messageIndex += packetBytes;
-    bytesLeft -= packetBytes;
-
-    // Send to any destinations that may have connected to us.
-    OSStatus result;
-    if ( data->endpoint ) {
-      result = MIDIReceived( data->endpoint, packetList );
-      if ( result != noErr ) {
-        errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
-        error( RtError::WARNING );
-      }
+  // Send to any destinations that may have connected to us.
+  if ( data->endpoint ) {
+    result = MIDIReceived( data->endpoint, &packetList );
+    if ( result != noErr ) {
+      errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
+      error( RtError::WARNING );
     }
+  }
 
-    // And send to an explicit destination port if we're connected.
-    if ( connected_ ) {
-      result = MIDISend( data->port, data->destinationId, packetList );
-      if ( result != noErr ) {
-        errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
-        error( RtError::WARNING );
-      }
+  // And send to an explicit destination port if we're connected.
+  if ( connected_ ) {
+    result = MIDISend( data->port, data->destinationId, &packetList );
+    if ( result != noErr ) {
+      errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
+      error( RtError::WARNING );
     }
   }
+
 }
 
 #endif  // __MACOSX_CORE__
@@ -840,6 +876,7 @@ extern "C" void *alsaMidiHandler( void *ptr )
   long nBytes;
   unsigned long long time, lastTime;
   bool continueSysex = false;
+  bool doDecode = false;
   RtMidiIn::MidiMessage message;
 
   snd_seq_event_t *ev;
@@ -881,9 +918,9 @@ extern "C" void *alsaMidiHandler( void *ptr )
 
     // This is a bit weird, but we now have to decode an ALSA MIDI
     // event (back) into MIDI bytes.  We'll ignore non-MIDI types.
-    if ( !continueSysex )
-      message.bytes.clear();
+    if ( !continueSysex ) message.bytes.clear();
 
+    doDecode = false;
     switch ( ev->type ) {
 
 		case SND_SEQ_EVENT_PORT_SUBSCRIBED:
@@ -896,21 +933,24 @@ extern "C" void *alsaMidiHandler( void *ptr )
 #if defined(__RTMIDI_DEBUG__)
       std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n";
       std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"
-                               << (int) ev->data.connect.sender.port
+                << (int) ev->data.connect.sender.port
                 << ", dest = " << (int) ev->data.connect.dest.client << ":"
-                               << (int) ev->data.connect.dest.port
+                << (int) ev->data.connect.dest.port
                 << std::endl;
 #endif
       break;
 
     case SND_SEQ_EVENT_QFRAME: // MIDI time code
-      if ( data->ignoreFlags & 0x02 ) break;
+      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
+      break;
 
     case SND_SEQ_EVENT_TICK: // MIDI timing tick
-      if ( data->ignoreFlags & 0x02 ) break;
+      if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
+      break;
 
     case SND_SEQ_EVENT_SENSING: // Active sensing
-      if ( data->ignoreFlags & 0x04 ) break;
+      if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;
+      break;
 
 		case SND_SEQ_EVENT_SYSEX:
       if ( (data->ignoreFlags & 0x01) ) break;
@@ -926,48 +966,53 @@ extern "C" void *alsaMidiHandler( void *ptr )
       }
 
     default:
+      doDecode = true;
+    }
+
+    if ( doDecode ) {
+
       nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
-      if ( nBytes <= 0 ) {
+      if ( nBytes > 0 ) {
+        // The ALSA sequencer has a maximum buffer size for MIDI sysex
+        // events of 256 bytes.  If a device sends sysex messages larger
+        // than this, they are segmented into 256 byte chunks.  So,
+        // we'll watch for this and concatenate sysex chunks into a
+        // single sysex message if necessary.
+        if ( !continueSysex )
+          message.bytes.assign( buffer, &buffer[nBytes] );
+        else
+          message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
+
+        continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
+        if ( !continueSysex ) {
+
+          // Calculate the time stamp:
+          message.timeStamp = 0.0;
+
+          // Method 1: Use the system time.
+          //(void)gettimeofday(&tv, (struct timezone *)NULL);
+          //time = (tv.tv_sec * 1000000) + tv.tv_usec;
+
+          // Method 2: Use the ALSA sequencer event time data.
+          // (thanks to Pedro Lopez-Cabanillas!).
+          time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
+          lastTime = time;
+          time -= apiData->lastTime;
+          apiData->lastTime = lastTime;
+          if ( data->firstMessage == true )
+            data->firstMessage = false;
+          else
+            message.timeStamp = time * 0.000001;
+        }
+        else {
 #if defined(__RTMIDI_DEBUG__)
-        std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
+          std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
 #endif
-        break;
+        }
       }
-
-      // The ALSA sequencer has a maximum buffer size for MIDI sysex
-      // events of 256 bytes.  If a device sends sysex messages larger
-      // than this, they are segmented into 256 byte chunks.  So,
-      // we'll watch for this and concatenate sysex chunks into a
-      // single sysex message if necessary.
-      if ( !continueSysex )
-        message.bytes.assign( buffer, &buffer[nBytes] );
-      else
-        message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
-
-      continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
-      if ( continueSysex )
-        break;
-
-      // Calculate the time stamp:
-      message.timeStamp = 0.0;
-
-      // Method 1: Use the system time.
-      //(void)gettimeofday(&tv, (struct timezone *)NULL);
-      //time = (tv.tv_sec * 1000000) + tv.tv_usec;
-
-      // Method 2: Use the ALSA sequencer event time data.
-      // (thanks to Pedro Lopez-Cabanillas!).
-      time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
-      lastTime = time;
-      time -= apiData->lastTime;
-      apiData->lastTime = lastTime;
-      if ( data->firstMessage == true )
-        data->firstMessage = false;
-      else
-        message.timeStamp = time * 0.000001;
     }
 
-    snd_seq_free_event(ev);
+    snd_seq_free_event( ev );
     if ( message.bytes.size() == 0 ) continue;
 
     if ( data->usingCallback && !continueSysex ) {
@@ -1247,6 +1292,7 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber )
   snd_seq_client_info_alloca( &cinfo );
   snd_seq_port_info_alloca( &pinfo );
 
+  std::string stringName;
   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
     int cnum = snd_seq_port_info_get_client( pinfo );
@@ -1255,14 +1301,15 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber )
     os << snd_seq_client_info_get_name( cinfo );
     os << ":";
     os << snd_seq_port_info_get_port( pinfo );
-    std::string stringName = os.str();
+    stringName = os.str();
     return stringName;
   }
 
   // If we get here, we didn't find a match.
   errorString_ = "RtMidiIn::getPortName: error looking for port name!";
-  error( RtError::INVALID_PARAMETER );
-  return 0;
+  error( RtError::WARNING );
+  return stringName;
+  //error( RtError::INVALID_PARAMETER );
 }
 
 //*********************************************************************//
@@ -1286,6 +1333,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
   snd_seq_client_info_alloca( &cinfo );
   snd_seq_port_info_alloca( &pinfo );
 
+  std::string stringName;
   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
     int cnum = snd_seq_port_info_get_client(pinfo);
@@ -1294,14 +1342,15 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
     os << snd_seq_client_info_get_name(cinfo);
     os << ":";
     os << snd_seq_port_info_get_port(pinfo);
-    std::string stringName = os.str();
+    stringName = os.str();
     return stringName;
   }
 
   // If we get here, we didn't find a match.
   errorString_ = "RtMidiOut::getPortName: error looking for port name!";
-  error( RtError::INVALID_PARAMETER );
-  return 0;
+  //error( RtError::INVALID_PARAMETER );
+  error( RtError::WARNING );
+  return stringName;
 }
 
 void RtMidiOut :: initialize( const std::string& clientName )
@@ -1591,12 +1640,11 @@ extern "C" void *irixMidiHandler( void *ptr )
     else if ( status < 0xC0 ) size = 3;
     else if ( status < 0xE0 ) size = 2;
     else if ( status < 0xF0 ) size = 3;
-    else if ( status < 0xF3 ) {
-      if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) {
+    else if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) {
         // A MIDI time code message and we're not ignoring it.
-        size = 3;
-      }
+        size = 2;
     }
+    else if ( status == 0xF2 ) size = 3;
     else if ( status == 0xF3 ) size = 2;
     else if ( status == 0xF8 ) {
       if ( !(data->ignoreFlags & 0x02) ) {
@@ -1735,14 +1783,17 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber )
 {
   int nPorts = mdInit();
 
+  std::string stringName;
   std::ostringstream ost;
   if ( portNumber >= nPorts ) {
     ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
     errorString_ = ost.str();
-    error( RtError::INVALID_PARAMETER );
+    //error( RtError::INVALID_PARAMETER );
+    error( RtError::WARNING );
   }
+  else
+    std::string stringName = std::string( mdGetName( portNumber ) );
 
-  std::string stringName = std::string( mdGetName( portNumber ) );
   return stringName;
 }
 
@@ -1762,14 +1813,17 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
 {
   int nPorts = mdInit();
 
+  std::string stringName;
   std::ostringstream ost;
   if ( portNumber >= nPorts ) {
     ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
     errorString_ = ost.str();
-    error( RtError::INVALID_PARAMETER );
+    //error( RtError::INVALID_PARAMETER );
+    error( RtError::WARNING );
   }
+  else
+    std::string stringName = std::string( mdGetName( portNumber ) );
 
-  std::string stringName = std::string( mdGetName( portNumber ) );
   return stringName;
 }
 
@@ -1918,8 +1972,8 @@ struct WinMidiData {
 
 static void CALLBACK midiInputCallback( HMIDIOUT hmin,
                                         UINT inputStatus, 
-                                        DWORD instancePtr,
-                                        DWORD midiMessage,
+                                        DWORD_PTR instancePtr,
+                                        DWORD_PTR midiMessage,
                                         DWORD timestamp )
 {
   if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;
@@ -1945,11 +1999,11 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin,
     if ( status < 0xC0 ) nBytes = 3;
     else if ( status < 0xE0 ) nBytes = 2;
     else if ( status < 0xF0 ) nBytes = 3;
-    else if ( status < 0xF3 ) {
-      // A MIDI time code message and we're ignoring it.
-      if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return;
-      nBytes = 3;
+    else if ( status == 0xF1 ) {
+      if ( data->ignoreFlags & 0x02 ) return;
+      else nBytes = 2;
     }
+    else if ( status == 0xF2 ) nBytes = 3;
     else if ( status == 0xF3 ) nBytes = 2;
     else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {
       // A MIDI timing tick message and we're ignoring it.
@@ -2048,8 +2102,8 @@ void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName
   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
   MMRESULT result = midiInOpen( &data->inHandle,
                                 portNumber,
-                                (DWORD)&midiInputCallback,
-                                (DWORD)&inputData_,
+                                (DWORD_PTR)&midiInputCallback,
+                                (DWORD_PTR)&inputData_,
                                 CALLBACK_FUNCTION );
   if ( result != MMSYSERR_NOERROR ) {
     errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port.";
@@ -2137,12 +2191,15 @@ unsigned int RtMidiIn :: getPortCount()
 
 std::string RtMidiIn :: getPortName( unsigned int portNumber )
 {
+  std::string stringName;
   unsigned int nDevices = midiInGetNumDevs();
   if ( portNumber >= nDevices ) {
     std::ostringstream ost;
     ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
     errorString_ = ost.str();
-    error( RtError::INVALID_PARAMETER );
+    //error( RtError::INVALID_PARAMETER );
+    error( RtError::WARNING );
+    return stringName;
   }
 
   MIDIINCAPS deviceCaps;
@@ -2155,7 +2212,7 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber )
   for( int i=0; i<MAXPNAMELEN; ++i )
     nameString[i] = (char)( deviceCaps.szPname[i] );
 
-  std::string stringName( nameString );
+  stringName = nameString;
   return stringName;
 }
 
@@ -2171,12 +2228,15 @@ unsigned int RtMidiOut :: getPortCount()
 
 std::string RtMidiOut :: getPortName( unsigned int portNumber )
 {
+  std::string stringName;
   unsigned int nDevices = midiOutGetNumDevs();
   if ( portNumber >= nDevices ) {
     std::ostringstream ost;
     ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
     errorString_ = ost.str();
-    error( RtError::INVALID_PARAMETER );
+    //error( RtError::INVALID_PARAMETER );
+    error( RtError::WARNING );
+    return stringName;
   }
 
   MIDIOUTCAPS deviceCaps;
@@ -2189,7 +2249,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
   for( int i=0; i<MAXPNAMELEN; ++i )
     nameString[i] = (char)( deviceCaps.szPname[i] );
 
-  std::string stringName( nameString );
+  stringName = nameString;
   return stringName;
 }
 
@@ -2272,7 +2332,7 @@ RtMidiOut :: ~RtMidiOut()
 
 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
 {
-  unsigned int nBytes = message->size();
+  unsigned int nBytes = static_cast<unsigned int>(message->size());
   if ( nBytes == 0 ) {
     errorString_ = "RtMidiOut::sendMessage: message argument is empty!";
     error( RtError::WARNING );

+ 4 - 4
RtMidi.h

@@ -8,7 +8,7 @@
     RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
 
     RtMidi: realtime MIDI i/o C++ classes
-    Copyright (c) 2003-2010 Gary P. Scavone
+    Copyright (c) 2003-2011 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -35,7 +35,7 @@
 */
 /**********************************************************************/
 
-// RtMidi: Version 1.0.11
+// RtMidi: Version 1.0.12
 
 #ifndef RTMIDI_H
 #define RTMIDI_H
@@ -154,7 +154,7 @@ class RtMidiIn : public RtMidi
 
   //! Return a string identifier for the specified MIDI input port number.
   /*!
-      An exception is thrown if an invalid port specifier is provided.
+      An empty string is returned if an invalid port specifier is provided.
   */
   std::string getPortName( unsigned int portNumber = 0 );
 
@@ -280,7 +280,7 @@ class RtMidiOut : public RtMidi
 
   //! Return a string identifier for the specified MIDI port type and number.
   /*!
-      An exception is thrown if an invalid port specifier is provided.
+      An empty string is returned if an invalid port specifier is provided.
   */
   std::string getPortName( unsigned int portNumber = 0 );
 

+ 91 - 32
doc/doxygen/Doxyfile

@@ -1,4 +1,4 @@
-# Doxyfile 1.5.8
+# Doxyfile 1.6.2
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project
@@ -31,7 +31,7 @@ PROJECT_NAME           = RtMidi
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.0.11
+PROJECT_NUMBER         = 1.0.12
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
 # base path where the generated documentation will be put. 
@@ -54,11 +54,11 @@ CREATE_SUBDIRS         = NO
 # information to generate all constant output in the proper language. 
 # The default language is English, other supported languages are: 
 # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
-# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
-# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
-# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
-# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
-# Spanish, Swedish, and Ukrainian.
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, 
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
 
 OUTPUT_LANGUAGE        = English
 
@@ -214,7 +214,8 @@ OPTIMIZE_OUTPUT_VHDL   = NO
 # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, 
 # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat 
 # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), 
-# use: inc=Fortran f=C
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
 
 EXTENSION_MAPPING      = 
 
@@ -386,6 +387,12 @@ HIDE_SCOPE_NAMES       = NO
 
 SHOW_INCLUDE_FILES     = YES
 
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen 
+# will list include files with double quotes in the documentation 
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
 # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
 # is inserted in the documentation for inline members.
 
@@ -405,6 +412,16 @@ SORT_MEMBER_DOCS       = NO
 
 SORT_BRIEF_DOCS        = NO
 
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
 # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
 # hierarchy of group names into alphabetical order. If set to NO (the default) 
 # the group names will appear in their defined order.
@@ -789,6 +806,12 @@ HTML_FOOTER            = footer.html
 
 HTML_STYLESHEET        = 
 
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 
+# page will contain the date and time when the page was generated. Setting 
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
 # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
 # files or namespaces will be aligned in HTML using tables. If set to 
 # NO a bullet list will be used.
@@ -890,7 +913,7 @@ QCH_FILE               =
 # Qt Help Project output. For more information please see 
 # http://doc.trolltech.com/qthelpproject.html#namespace
 
-QHP_NAMESPACE          = 
+QHP_NAMESPACE          = org.doxygen.Project
 
 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
 # Qt Help Project output. For more information please see 
@@ -922,6 +945,23 @@ QHP_SECT_FILTER_ATTRS  =
 
 QHG_LOCATION           = 
 
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files  
+# will be generated, which together with the HTML files, form an Eclipse help  
+# plugin. To install this plugin and make it available under the help contents 
+# menu in Eclipse, the contents of the directory containing the HTML and XML 
+# files needs to be copied into the plugins directory of eclipse. The name of 
+# the directory within the plugins directory should be the same as 
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin 
+# the directory name containing the HTML and XML files should also have 
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
 # The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
 # top of each HTML page. The value NO (the default) enables the index and 
 # the value YES disables it.
@@ -935,21 +975,19 @@ ENUM_VALUES_PER_LINE   = 4
 
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
 # structure should be generated to display hierarchical information. 
-# If the tag value is set to FRAME, a side panel will be generated 
+# If the tag value is set to YES, a side panel will be generated 
 # containing a tree-like index structure (just like the one that 
 # is generated for HTML Help). For this to work a browser that supports 
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
-# probably better off using the HTML help feature. Other possible values 
-# for this tag are: HIERARCHIES, which will generate the Groups, Directories, 
-# and Class Hierarchy pages using a tree view instead of an ordered list; 
-# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which 
-# disables this behavior completely. For backwards compatibility with previous 
-# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE 
-# respectively.
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 
+# Windows users are probably better off using the HTML help feature.
 
 GENERATE_TREEVIEW      = NO
 
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, 
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
 # used to set the initial width (in pixels) of the frame in which the tree 
 # is shown.
@@ -964,6 +1002,26 @@ TREEVIEW_WIDTH         = 250
 
 FORMULA_FONTSIZE       = 10
 
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript 
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should 
+# typically be disabled. For large projects the javascript based search engine 
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index 
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup 
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -980,7 +1038,10 @@ GENERATE_LATEX         = NO
 LATEX_OUTPUT           = latex
 
 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
-# invoked. If left blank `latex' will be used as the default command name.
+# invoked. If left blank `latex' will be used as the default command name. 
+# Note that when enabling USE_PDFLATEX this option is only used for 
+# generating bitmaps for formulas in the HTML output, but not in the 
+# Makefile that is written to the output directory.
 
 LATEX_CMD_NAME         = latex
 
@@ -1040,6 +1101,13 @@ LATEX_BATCHMODE        = NO
 
 LATEX_HIDE_INDICES     = NO
 
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
 #---------------------------------------------------------------------------
 # configuration options related to the RTF output
 #---------------------------------------------------------------------------
@@ -1191,7 +1259,7 @@ PERLMOD_PRETTY         = YES
 PERLMOD_MAKEVAR_PREFIX = 
 
 #---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
+# Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
 # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
@@ -1257,7 +1325,7 @@ EXPAND_AS_DEFINED      =
 SKIP_FUNCTION_MACROS   = YES
 
 #---------------------------------------------------------------------------
-# Configuration::additions related to external references   
+# Configuration::additions related to external references
 #---------------------------------------------------------------------------
 
 # The TAGFILES option can be used to specify one or more tagfiles. 
@@ -1300,7 +1368,7 @@ EXTERNAL_GROUPS        = YES
 PERL_PATH              = /usr/bin/perl
 
 #---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
+# Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
 # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
@@ -1492,12 +1560,3 @@ GENERATE_LEGEND        = YES
 # the various graphs.
 
 DOT_CLEANUP            = YES
-
-#---------------------------------------------------------------------------
-# Options related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be 
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE           = NO

+ 1 - 1
doc/doxygen/footer.html

@@ -1,7 +1,7 @@
 <HR>
 
 <table><tr><td><img src="../images/mcgill.gif" width=165></td>
-  <td>&copy;2003-2010 Gary P. Scavone, McGill University. All Rights Reserved.<br>
+  <td>&copy;2003-2011 Gary P. Scavone, McGill University. All Rights Reserved.<br>
   Maintained by Gary P. Scavone, gary at music.mcgill.ca</td></tr>
 </table>
 

+ 8 - 7
doc/doxygen/tutorial.txt

@@ -17,7 +17,7 @@ MIDI input and output functionality are separated into two classes, RtMidiIn and
 
 \section download Download
 
-Latest Release (29 January 2010): <A href="http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.11.tar.gz">Version 1.0.11</A>
+Latest Release (17 February 2011): <A href="http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.12.tar.gz">Version 1.0.12</A>
 
 \section start Getting Started
 
@@ -408,23 +408,24 @@ The \c configure script provides support for the MinGW compiler.
 
 Many thanks to the following people for providing bug fixes and improvements:
 <UL>
-<LI>John Dey (OS-X timestamps)</LI>
+<LI>Jean-Baptiste Berruchon (Windows sysex code)</LI>
 <LI>Pedro Lopez-Cabanillas (ALSA sequencer API, client naming)</LI>
-<LI>Casey Tucker (OS-X driver information)</LI>
+<LI>Jason Champion (MSW project file for library build)</LI>
 <LI>Eduardo Coutinho (Windows device names)</LI>
-<LI>Jean-Baptiste Berruchon (Windows sysex code)</LI>
+<LI>Paul Dean (increment optimization)</LI>
+<LI>John Dey (OS-X timestamps)</LI>
 <LI>Christoph Eckert (ALSA sysex fixes)</LI>
 <LI>Immanuel Litzroth (OS-X sysex fix)</LI>
+<LI>Jon McCormack (Snow Leopard updates)</LI>
 <LI>Axel Schmidt (client naming)</LI>
+<LI>Casey Tucker (OS-X driver information, sysex sending)</LI>
 <LI>Bastiaan Verreijt (Windows sysex multi-buffer code)</LI>
-<LI>Jon McCormack (Snow Leopard updates)</LI>
-<LI>Paul Dean (increment optimization)</LI>
 </UL>
 
 \section license License
 
     RtMidi: realtime MIDI i/o C++ classes<BR>
-    Copyright (c) 2003-2010 Gary P. Scavone
+    Copyright (c) 2003-2011 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files

+ 8 - 1
doc/release.txt

@@ -1,6 +1,13 @@
 RtMidi - a set of C++ classes that provides a common API for realtime MIDI input/output across Linux (ALSA), SGI, Macintosh OS X (CoreMidi), and Windows (Multimedia) operating systems.
 
-By Gary P. Scavone, 2003-2010.
+By Gary P. Scavone, 2003-2011.
+
+v1.0.12: (17 February 2011)
+- Windows 64-bit pointer fixes (thanks to Ward Kockelkorn)
+- removed possible exceptions from getPortName() functions
+- changed sysex sends in OS-X to use MIDISendSysex() function (thanks to Casey Tucker)
+- bug fixes to time code parsing in OS-X and ALSA (thanks to Greg)
+- added MSW project file to build as library (into lib/ directory ... thanks to Jason Champion)
 
 v1.0.11: (29 January 2010)
 - added CoreServices/CoreServices.h include for OS-X 10.6 and gcc4.2 compile (thanks to Jon McCormack)

+ 1 - 0
msw/readme

@@ -0,0 +1 @@
+This directory contains a Visual Studio 2008 project, contributed by Jason Champion, to build rtmidi as a library.  The library builds to the <rtmidi-x.x.x>\lib directory.

+ 20 - 0
msw/rtmidilib.sln

@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtmidilib", "rtmidilib.vcproj", "{EBFE5EB3-182A-47A6-922B-52ECF777F6A3}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{EBFE5EB3-182A-47A6-922B-52ECF777F6A3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{EBFE5EB3-182A-47A6-922B-52ECF777F6A3}.Debug|Win32.Build.0 = Debug|Win32
+		{EBFE5EB3-182A-47A6-922B-52ECF777F6A3}.Release|Win32.ActiveCfg = Release|Win32
+		{EBFE5EB3-182A-47A6-922B-52ECF777F6A3}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 178 - 0
msw/rtmidilib.vcproj

@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="rtmidilib"
+	ProjectGUID="{EBFE5EB3-182A-47A6-922B-52ECF777F6A3}"
+	RootNamespace="rtmidilib"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="__WINDOWS_MM__"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\rtmidid.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				PreprocessorDefinitions="__WINDOWS_MM__"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\rtmidi.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\RtMidi.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath="..\RtError.h"
+				>
+			</File>
+			<File
+				RelativePath="..\RtMidi.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 2 - 2
readme

@@ -1,6 +1,6 @@
 RtMidi - a set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA), SGI, Macintosh OS X (CoreMidi), and Windows (Multimedia) operating systems.
 
-By Gary P. Scavone, 2003-2010.
+By Gary P. Scavone, 2003-2011.
 
 This distribution of RtMidi contains the following:
 
@@ -30,7 +30,7 @@ LEGAL AND ETHICAL:
 The RtMidi license is similar to the the MIT License, with the added "feature" that modifications be sent to the developer.
 
     RtMidi: realtime MIDI i/o C++ classes
-    Copyright (c) 2003-2010 Gary P. Scavone
+    Copyright (c) 2003-2011 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files

+ 6 - 3
tests/Makefile.in

@@ -37,9 +37,12 @@ sysextest : sysextest.cpp $(OBJECTS)
 	$(CC) $(CFLAGS) $(DEFS) -o sysextest sysextest.cpp $(OBJECT_PATH)/RtMidi.o $(LIBRARY)
 
 clean : 
-	-rm $(OBJECT_PATH)/*.o
-	-rm $(PROGRAMS) *.exe
-	-rm -f *~
+	$(RM) -f $(OBJECT_PATH)/*.o
+	$(RM) -f $(PROGRAMS) *.exe
+	$(RM) -f *~
+
+distclean: clean
+	$(RM) -f Makefile
 
 strip : 
 	strip $(PROGRAMS)

+ 61 - 32
tests/cmidiin.cpp

@@ -28,6 +28,11 @@ void mycallback( double deltatime, std::vector< unsigned char > *message, void *
     std::cout << "stamp = " << deltatime << std::endl;
 }
 
+// This function should be embedded in a try/catch block in case of
+// an exception.  It offers the user a choice of MIDI ports to open.
+// It returns false if there are no ports available.
+bool chooseMidiPort( RtMidiIn *rtmidi );
+
 int main( int argc, char *argv[] )
 {
   RtMidiIn *midiin = 0;
@@ -35,48 +40,72 @@ int main( int argc, char *argv[] )
   // Minimal command-line check.
   if ( argc > 2 ) usage();
 
-  // RtMidiIn constructor
   try {
+
+    // RtMidiIn constructor
     midiin = new RtMidiIn();
-  }
-  catch ( RtError &error ) {
-    error.printMessage();
-    exit( EXIT_FAILURE );
-  }
 
-  // Check available ports vs. specified.
-  unsigned int port = 0;
-  unsigned int nPorts = midiin->getPortCount();
-  if ( argc == 2 ) port = (unsigned int) atoi( argv[1] );
-  if ( port >= nPorts ) {
-    delete midiin;
-    std::cout << "Invalid port specifier!\n";
-    usage();
-  }
+    // Call function to select port.
+    if ( chooseMidiPort( midiin ) == false ) goto cleanup;
 
-  try {
-    midiin->openPort( port );
-  }
-  catch ( RtError &error ) {
-    error.printMessage();
-    goto cleanup;
-  }
+    // Set our callback function.  This should be done immediately after
+    // opening the port to avoid having incoming messages written to the
+    // queue instead of sent to the callback function.
+    midiin->setCallback( &mycallback );
 
-  // Set our callback function.  This should be done immediately after
-  // opening the port to avoid having incoming messages written to the
-  // queue instead of sent to the callback function.
-  midiin->setCallback( &mycallback );
+    // Don't ignore sysex, timing, or active sensing messages.
+    midiin->ignoreTypes( false, false, false );
 
-  // Don't ignore sysex, timing, or active sensing messages.
-  midiin->ignoreTypes( false, false, false );
+    std::cout << "\nReading MIDI input ... press <enter> to quit.\n";
+    char input;
+    std::cin.get(input);
 
-  std::cout << "\nReading MIDI input ... press <enter> to quit.\n";
-  char input;
-  std::cin.get(input);
+  } catch ( RtError &error ) {
+    error.printMessage();
+  }
 
-  // Clean up
  cleanup:
+
   delete midiin;
 
   return 0;
 }
+
+bool chooseMidiPort( RtMidiIn *rtmidi )
+{
+  std::cout << "\nWould you like to open a virtual input port? [y/N] ";
+
+  std::string keyHit;
+  std::getline( std::cin, keyHit );
+  if ( keyHit == "y" ) {
+    rtmidi->openVirtualPort();
+    return true;
+  }
+
+  std::string portName;
+  unsigned int i = 0, nPorts = rtmidi->getPortCount();
+  if ( nPorts == 0 ) {
+    std::cout << "No input ports available!" << std::endl;
+    return false;
+  }
+
+  if ( nPorts == 1 ) {
+    std::cout << "\nOpening " << rtmidi->getPortName() << std::endl;
+  }
+  else {
+    for ( i=0; i<nPorts; i++ ) {
+      portName = rtmidi->getPortName(i);
+      std::cout << "  Input port #" << i << ": " << portName << '\n';
+    }
+
+    do {
+      std::cout << "\nChoose a port number: ";
+      std::cin >> i;
+    } while ( i >= nPorts );
+  }
+
+  std::getline( std::cin, keyHit );  // used to clear out stdin
+  rtmidi->openPort( i );
+
+  return true;
+}

+ 17 - 1
tests/midiout.cpp

@@ -54,6 +54,12 @@ int main( int argc, char *argv[] )
   message.push_back( 5 );
   midiout->sendMessage( &message );
 
+  SLEEP( 500 );
+
+  message[0] = 0xF1;
+  message[1] = 60;
+  midiout->sendMessage( &message );
+
   // Control Change: 176, 7, 100 (volume)
   message[0] = 176;
   message[1] = 7;
@@ -74,7 +80,17 @@ int main( int argc, char *argv[] )
   message[2] = 40;
   midiout->sendMessage( &message );
 
-  // Sysex: 240, 67, 16, 4, 3, 2, 247
+  SLEEP( 500 );
+
+  // Control Change: 176, 7, 40
+  message[0] = 176;
+  message[1] = 7;
+  message[2] = 40;
+  midiout->sendMessage( &message );
+
+  SLEEP( 500 );
+
+  // Sysex: 240, 67, 4, 3, 2, 247
   message[0] = 240;
   message[1] = 67;
   message[2] = 4;

+ 21 - 41
tests/midiprobe.cpp

@@ -13,57 +13,37 @@ int main()
   RtMidiIn  *midiin = 0;
   RtMidiOut *midiout = 0;
 
-  // RtMidiIn constructor
   try {
+
+    // RtMidiIn constructor ... exception possible
     midiin = new RtMidiIn();
-  }
-  catch ( RtError &error ) {
-    error.printMessage();
-    exit( EXIT_FAILURE );
-  }
 
-  // Check inputs.
-  unsigned int nPorts = midiin->getPortCount();
-  std::cout << "\nThere are " << nPorts << " MIDI input sources available.\n";
-  std::string portName;
-  unsigned int i;
-  for ( i=0; i<nPorts; i++ ) {
-    try {
-      portName = midiin->getPortName(i);
-    }
-    catch ( RtError &error ) {
-      error.printMessage();
-      goto cleanup;
+    // Check inputs.
+    unsigned int nPorts = midiin->getPortCount();
+    std::cout << "\nThere are " << nPorts << " MIDI input sources available.\n";
+
+    for ( unsigned i=0; i<nPorts; i++ ) {
+      std::string portName = midiin->getPortName(i);
+      std::cout << "  Input Port #" << i+1 << ": " << portName << '\n';
     }
-    std::cout << "  Input Port #" << i+1 << ": " << portName << '\n';
-  }
 
-  // RtMidiOut constructor
-  try {
+    // RtMidiOut constructor ... exception possible
     midiout = new RtMidiOut();
-  }
-  catch ( RtError &error ) {
-    error.printMessage();
-    exit( EXIT_FAILURE );
-  }
 
-  // Check outputs.
-  nPorts = midiout->getPortCount();
-  std::cout << "\nThere are " << nPorts << " MIDI output ports available.\n";
-  for ( i=0; i<nPorts; i++ ) {
-    try {
-      portName = midiout->getPortName(i);
-    }
-    catch ( RtError &error ) {
-      error.printMessage();
-      goto cleanup;
+    // Check outputs.
+    nPorts = midiout->getPortCount();
+    std::cout << "\nThere are " << nPorts << " MIDI output ports available.\n";
+
+    for ( unsigned i=0; i<nPorts; i++ ) {
+      std::string portName = midiout->getPortName(i);
+      std::cout << "  Output Port #" << i+1 << ": " << portName << std::endl;
     }
-    std::cout << "  Output Port #" << i+1 << ": " << portName << '\n';
+    std::cout << std::endl;
+
+  } catch ( RtError &error ) {
+    error.printMessage();
   }
-  std::cout << '\n';
 
-  // Clean up
- cleanup:
   delete midiin;
   delete midiout;