Browse Source

Version 1.0.12

Gary Scavone 11 years ago
parent
commit
8fec3d71d4
14 changed files with 616 additions and 262 deletions
  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;