瀏覽代碼

Version 1.0.8

Gary Scavone 11 年之前
父節點
當前提交
cb9cf7ddba
共有 18 個文件被更改,包括 290 次插入152 次删除
  1. 157 86
      RtMidi.cpp
  2. 17 14
      RtMidi.h
  3. 0 0
      config/config.guess
  4. 0 0
      config/config.sub
  5. 0 0
      config/install.sh
  6. 21 16
      configure.ac
  7. 44 7
      doc/doxygen/Doxyfile
  8. 1 1
      doc/doxygen/footer.html
  9. 21 10
      doc/doxygen/tutorial.txt
  10. 8 1
      doc/release.txt
  11. 4 4
      readme
  12. 3 5
      tests/Makefile.in
  13. 2 1
      tests/cmidiin.cpp
  14. 1 0
      tests/midiout.cpp
  15. 1 0
      tests/midiprobe.cpp
  16. 2 1
      tests/qmidiin.cpp
  17. 3 1
      tests/sysextest.cpp
  18. 5 5
      tests/sysextest.dsp

+ 157 - 86
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-2007 Gary P. Scavone
+    Copyright (c) 2003-2009 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.7
+// RtMidi: Version 1.0.8
 
 #include "RtMidi.h"
 #include <sstream>
@@ -69,9 +69,9 @@ void RtMidi :: error( RtError::Type type )
 //  Common RtMidiIn Definitions
 //*********************************************************************//
 
-RtMidiIn :: RtMidiIn() : RtMidi()
+RtMidiIn :: RtMidiIn( const std::string clientName ) : RtMidi()
 {
-  this->initialize();
+  this->initialize( clientName );
 }
 
 void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData )
@@ -144,9 +144,9 @@ double RtMidiIn :: getMessage( std::vector<unsigned char> *message )
 //  Common RtMidiOut Definitions
 //*********************************************************************//
 
-RtMidiOut :: RtMidiOut() : RtMidi()
+RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi()
 {
-  this->initialize();
+  this->initialize( clientName );
 }
 
 
@@ -187,20 +187,24 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
   RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (procRef);
   CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
 
-  bool continueSysex = false;
   unsigned char status;
   unsigned short nBytes, iByte, size;
   unsigned long long time;
-  RtMidiIn::MidiMessage message;
+
+  bool& continueSysex = data->continueSysex;
+  RtMidiIn::MidiMessage& message = data->message;
+
   const MIDIPacket *packet = &list->packet[0];
   for ( unsigned int i=0; i<list->numPackets; ++i ) {
 
     // My interpretation of the CoreMIDI documentation: all message
     // types, except sysex, are complete within a packet and there may
     // be several of them in a single packet.  Sysex messages can be
-    // broken across multiple packets but are bundled alone within a
-    // packet.  I'm assuming that sysex messages, if segmented, must
-    // be complete within the same MIDIPacketList.
+    // broken across multiple packets and PacketLists but are bundled
+    // alone within each packet (these packets do not contain other
+    // message types).  If sysex messages are split across multiple
+    // MIDIPacketLists, they must be handled by multiple calls to this
+    // function.
 
     nBytes = packet->length;
     if ( nBytes == 0 ) continue;
@@ -220,12 +224,13 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
     iByte = 0;
     if ( continueSysex ) {
       // We have a continuing, segmented sysex message.
-      if ( !(data->ignoreFlags & 0x01) ) {
+      if ( !( data->ignoreFlags & 0x01 ) ) {
         // If we're not ignoring sysex messages, copy the entire packet.
         for ( unsigned int j=0; j<nBytes; j++ )
           message.bytes.push_back( packet->data[j] );
       }
-      if ( packet->data[nBytes] == 0xF7 ) continueSysex = false;
+      continueSysex = packet->data[nBytes-1] != 0xF7;
+
       if ( !continueSysex ) {
         // If not a continuing sysex message, invoke the user callback function or queue the message.
         if ( data->usingCallback && message.bytes.size() > 0 ) {
@@ -259,7 +264,7 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
             iByte = nBytes;
           }
           else size = nBytes - iByte;
-          if ( packet->data[nBytes] == 0xF7 ) continueSysex = false;
+          continueSysex =  packet->data[nBytes-1] != 0xF7;
         }
         else if ( status < 0xF3 ) {
           if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) {
@@ -311,11 +316,11 @@ void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef
   }
 }
 
-void RtMidiIn :: initialize( void )
+void RtMidiIn :: initialize( const std::string& clientName )
 {
   // Set up our client.
   MIDIClientRef client;
-  OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client );
+  OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
   if ( result != noErr ) {
     errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object.";
     error( RtError::DRIVER_ERROR );
@@ -329,7 +334,7 @@ void RtMidiIn :: initialize( void )
   inputData_.apiData = (void *) data;
 }
 
-void RtMidiIn :: openPort( unsigned int portNumber )
+void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
@@ -352,7 +357,9 @@ void RtMidiIn :: openPort( unsigned int portNumber )
 
   MIDIPortRef port;
   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
-  OSStatus result = MIDIInputPortCreate( data->client, CFSTR("RtMidi MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port );
+  OSStatus result = MIDIInputPortCreate( data->client, 
+                                         CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
+                                         midiInputCallback, (void *)&inputData_, &port );
   if ( result != noErr ) {
     MIDIClientDispose( data->client );
     errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port.";
@@ -479,11 +486,11 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
   return stringName;
 }
 
-void RtMidiOut :: initialize( void )
+void RtMidiOut :: initialize( const std::string& clientName )
 {
   // Set up our client.
   MIDIClientRef client;
-  OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client );
+  OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
   if ( result != noErr ) {
     errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object.";
     error( RtError::DRIVER_ERROR );
@@ -496,7 +503,7 @@ void RtMidiOut :: initialize( void )
   apiData_ = (void *) data;
 }
 
-void RtMidiOut :: openPort( unsigned int portNumber )
+void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
@@ -519,7 +526,9 @@ void RtMidiOut :: openPort( unsigned int portNumber )
 
   MIDIPortRef port;
   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
-  OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("RtMidi Virtual MIDI Output Port"), &port );
+  OSStatus result = MIDIOutputPortCreate( data->client, 
+                                          CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
+                                          &port );
   if ( result != noErr ) {
     MIDIClientDispose( data->client );
     errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port.";
@@ -588,33 +597,59 @@ RtMidiOut :: ~RtMidiOut()
 
 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.
   unsigned int nBytes = message->size();
-  // Pad the buffer for extra (unknown) structure data.
-  Byte buffer[nBytes+32];
-  MIDIPacketList *pktlist = (MIDIPacketList *) buffer;
-  MIDIPacket *curPacket = MIDIPacketListInit( pktlist );
+  if ( nBytes == 0 ) {
+    errorString_ = "RtMidiOut::sendMessage: no data in message argument!";      
+    error( RtError::WARNING );
+    return;
+  }
 
-  MIDITimeStamp timeStamp = 0;
-  curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) );
+  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;
+  MIDITimeStamp timeStamp = 0;
   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
 
-  // Send to any destinations that may have connected to us.
-  OSStatus result;
-  if ( data->endpoint ) {
-    result = MIDIReceived( data->endpoint, pktlist );
-    if ( result != noErr ) {
-      errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
-      error( RtError::WARNING );
+  while ( bytesLeft > 0 ) {
+
+    packetBytes = ( bytesLeft > 32736 ) ? 32736 : bytesLeft;
+    Byte buffer[packetBytes + 32]; // extra memory for other structure variables
+    MIDIPacketList *packetList = (MIDIPacketList *) buffer;
+    MIDIPacket *curPacket = MIDIPacketListInit( packetList );
+
+    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 );
+      }
     }
-  }
 
-  // And send to an explicit destination port if we're connected.
-  if ( connected_ ) {
-    result = MIDISend( data->port, data->destinationId, pktlist );
-    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 );
+      }
     }
   }
 }
@@ -713,7 +748,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.
-    message.bytes.clear();
+    if ( !continueSysex )
+      message.bytes.clear();
+
     switch ( ev->type ) {
 
 		case SND_SEQ_EVENT_PORT_SUBSCRIBED:
@@ -723,8 +760,15 @@ extern "C" void *alsaMidiHandler( void *ptr )
       break;
 
 		case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
+#if defined(__RTMIDI_DEBUG__)
       std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n";
-      data->doInput = false;
+      // FIXME: this is called for all unsubscribe events, even ones
+      //not related to this particular connection.  As it stands, I
+      //see no data provided in the "source" and "dest" fields so
+      //there is nothing we can do about this at this time.
+      // std::cout << "sender = " << ev->source.client << ", dest = " << ev->dest.port << std::endl;
+#endif
+      //data->doInput = false;
       break;
 
     case SND_SEQ_EVENT_QFRAME: // MIDI time code
@@ -768,7 +812,7 @@ extern "C" void *alsaMidiHandler( void *ptr )
       else
         message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
 
-      continueSysex = ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() != 0xF7 );
+      continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
       if ( continueSysex )
         break;
 
@@ -794,7 +838,7 @@ extern "C" void *alsaMidiHandler( void *ptr )
     snd_seq_free_event(ev);
     if ( message.bytes.size() == 0 ) continue;
 
-    if ( data->usingCallback ) {
+    if ( data->usingCallback && !continueSysex ) {
       RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
       callback( message.timeStamp, &message.bytes, data->userData );
     }
@@ -813,10 +857,10 @@ extern "C" void *alsaMidiHandler( void *ptr )
   return 0;
 }
 
-void RtMidiIn :: initialize( void )
+void RtMidiIn :: initialize( const std::string& clientName )
 {
   // Set up the ALSA sequencer client.
-	snd_seq_t *seq;
+  snd_seq_t *seq;
   int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
   if ( result < 0 ) {
     errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object.";
@@ -824,7 +868,7 @@ void RtMidiIn :: initialize( void )
 	}
 
   // Set client name.
-  snd_seq_set_client_name(seq, "RtMidi Input Client");
+  snd_seq_set_client_name( seq, clientName.c_str() );
 
   // Save our api-specific connection information.
   AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
@@ -860,7 +904,10 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int
 		snd_seq_port_info_set_client( pinfo, client );
 		snd_seq_port_info_set_port( pinfo, -1 );
 		while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
-      if ( !PORT_TYPE( pinfo, type ) )  continue;
+      unsigned int atyp = snd_seq_port_info_get_type( pinfo );
+      if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue;
+      unsigned int caps = snd_seq_port_info_get_capability( pinfo );
+      if ( ( caps & type ) != type ) continue;
       if ( count == portNumber ) return 1;
       count++;
 		}
@@ -871,7 +918,7 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int
   return 0;
 }
 
-void RtMidiIn :: openPort( unsigned int portNumber )
+void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
@@ -913,7 +960,7 @@ void RtMidiIn :: openPort( unsigned int portNumber )
     snd_seq_port_info_set_timestamping(pinfo, 1);
     snd_seq_port_info_set_timestamp_real(pinfo, 1);    
     snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
-    snd_seq_port_info_set_name(pinfo, "RtMidi Input");
+    snd_seq_port_info_set_name(pinfo,  portName.c_str() );
     data->vport = snd_seq_create_port(data->seq, pinfo);
   
     if ( data->vport < 0 ) {
@@ -1049,12 +1096,20 @@ unsigned int RtMidiIn :: getPortCount()
 
 std::string RtMidiIn :: getPortName( unsigned int portNumber )
 {
-	snd_seq_port_info_t *pinfo;
-	snd_seq_port_info_alloca( &pinfo );
+  snd_seq_client_info_t *cinfo;
+  snd_seq_port_info_t *pinfo;
+  snd_seq_client_info_alloca( &cinfo );
+  snd_seq_port_info_alloca( &pinfo );
 
   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
-    std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) );
+    int cnum = snd_seq_port_info_get_client( pinfo );
+    snd_seq_get_any_client_info( data->seq, cnum, cinfo );
+    std::ostringstream os;
+    os << snd_seq_client_info_get_name( cinfo );
+    os << ":";
+    os << snd_seq_port_info_get_port( pinfo );
+    std::string stringName = os.str();
     return stringName;
   }
 
@@ -1080,12 +1135,20 @@ unsigned int RtMidiOut :: getPortCount()
 
 std::string RtMidiOut :: getPortName( unsigned int portNumber )
 {
-	snd_seq_port_info_t *pinfo;
-	snd_seq_port_info_alloca( &pinfo );
+  snd_seq_client_info_t *cinfo;
+  snd_seq_port_info_t *pinfo;
+  snd_seq_client_info_alloca( &cinfo );
+  snd_seq_port_info_alloca( &pinfo );
 
   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
-    std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) );
+    int cnum = snd_seq_port_info_get_client(pinfo);
+    snd_seq_get_any_client_info( data->seq, cnum, cinfo );
+    std::ostringstream os;
+    os << snd_seq_client_info_get_name(cinfo);
+    os << ":";
+    os << snd_seq_port_info_get_port(pinfo);
+    std::string stringName = os.str();
     return stringName;
   }
 
@@ -1095,18 +1158,18 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
   return 0;
 }
 
-void RtMidiOut :: initialize( void )
+void RtMidiOut :: initialize( const std::string& clientName )
 {
   // Set up the ALSA sequencer client.
-	snd_seq_t *seq;
-  int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0);
+  snd_seq_t *seq;
+  int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
   if ( result < 0 ) {
     errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object.";
     error( RtError::DRIVER_ERROR );
 	}
 
   // Set client name.
-  snd_seq_set_client_name(seq, "RtMidi Output Client");
+  snd_seq_set_client_name( seq, clientName.c_str() );
 
   // Save our api-specific connection information.
   AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
@@ -1131,7 +1194,7 @@ void RtMidiOut :: initialize( void )
   apiData_ = (void *) data;
 }
 
-void RtMidiOut :: openPort( unsigned int portNumber )
+void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
@@ -1161,7 +1224,7 @@ void RtMidiOut :: openPort( unsigned int portNumber )
   sender.client = snd_seq_client_id( data->seq );
 
   if ( data->vport < 0 ) {
-    data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output",
+    data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
                                               SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
                                               SND_SEQ_PORT_TYPE_MIDI_GENERIC );
     if ( data->vport < 0 ) {
@@ -1423,7 +1486,7 @@ extern "C" void *irixMidiHandler( void *ptr )
   return 0;
 }
 
-void RtMidiIn :: initialize( void )
+void RtMidiIn :: initialize( const std::string& /*clientName*/ )
 {
   // Initialize the Irix MIDI system.  At the moment, we will not
   // worry about a return value of zero (ports) because there is a
@@ -1436,7 +1499,7 @@ void RtMidiIn :: initialize( void )
   inputData_.apiData = (void *) data;
 }
 
-void RtMidiIn :: openPort( unsigned int portNumber )
+void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
@@ -1564,7 +1627,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
   return stringName;
 }
 
-void RtMidiOut :: initialize( void )
+void RtMidiOut :: initialize( const std::string& /*clientName*/ )
 {
   // Initialize the Irix MIDI system.  At the moment, we will not
   // worry about a return value of zero (ports) because there is a
@@ -1576,7 +1639,7 @@ void RtMidiOut :: initialize( void )
   apiData_ = (void *) data;
 }
 
-void RtMidiOut :: openPort( unsigned int portNumber )
+void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
@@ -1754,21 +1817,31 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin,
     unsigned char *ptr = (unsigned char *) &midiMessage;
     for ( int i=0; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ );
   }
-  else if ( !(data->ignoreFlags & 0x01) ) {
-    // Sysex message and we're not ignoring it
-    MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
-    for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ )
-      apiData->message.bytes.push_back( sysex->lpData[i] );
+  else { // Sysex message ( MIM_LONGDATA )
+    MIDIHDR *sysex = ( MIDIHDR *) midiMessage; 
+    if ( !( data->ignoreFlags & 0x01 ) ) {  
+      // Sysex message and we're not ignoring it
+      for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ )
+        apiData->message.bytes.push_back( sysex->lpData[i] );
+    }
 
-    // When the callback has to be unaffected (application closes), 
-    // it seems WinMM calls it with an empty sysex to de-queue the buffer
-    // If the buffer is requeued afer that message, the PC suddenly reboots
-    // after one or two minutes (JB).
+    // The WinMM API requires that the sysex buffer be requeued after
+    // input of each sysex message.  Even if we are ignoring sysex
+    // messages, we still need to requeue the buffer in case the user
+    // decides to not ignore sysex messages in the future.  However,
+    // it seems that WinMM calls this function with an empty sysex
+    // buffer when an application closes and in this case, we should
+    // avoid requeueing it, else the computer suddenly reboots after
+    // one or two minutes.
     if ( apiData->sysexBuffer->dwBytesRecorded > 0 ) {
+      //if ( sysex->dwBytesRecorded > 0 ) {
       MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) );
       if ( result != MMSYSERR_NOERROR )
         std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
+
+      if ( data->ignoreFlags & 0x01 ) return;
     }
+    else return;
   }
 
   if ( data->usingCallback ) {
@@ -1783,13 +1856,11 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin,
       std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
   }
 
-  // Clear the vector for the next input message.  Note that doing
-  // this here allows our code to work for sysex messages which are
-  // segmented across multiple buffers.
+  // Clear the vector for the next input message.
   apiData->message.bytes.clear();
 }
 
-void RtMidiIn :: initialize( void )
+void RtMidiIn :: initialize( const std::string& /*clientName*/ )
 {
   // We'll issue a warning here if no devices are available but not
   // throw an error since the user can plugin something later.
@@ -1806,7 +1877,7 @@ void RtMidiIn :: initialize( void )
   data->message.bytes.clear();  // needs to be empty for first input message
 }
 
-void RtMidiIn :: openPort( unsigned int portNumber )
+void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
@@ -1840,8 +1911,8 @@ void RtMidiIn :: openPort( unsigned int portNumber )
 
   // Allocate and init the sysex buffer.
   data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
-  data->sysexBuffer->lpData = new char[1024];
-  data->sysexBuffer->dwBufferLength = 1024;
+  data->sysexBuffer->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
+  data->sysexBuffer->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
   data->sysexBuffer->dwFlags = 0;
 
   result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) );
@@ -1970,7 +2041,7 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
   return stringName;
 }
 
-void RtMidiOut :: initialize( void )
+void RtMidiOut :: initialize( const std::string& /*clientName*/ )
 {
   // We'll issue a warning here if no devices are available but not
   // throw an error since the user can plug something in later.
@@ -1985,7 +2056,7 @@ void RtMidiOut :: initialize( void )
   apiData_ = (void *) data;
 }
 
-void RtMidiOut :: openPort( unsigned int portNumber )
+void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
 {
   if ( connected_ ) {
     errorString_ = "RtMidiOut::openPort: a valid connection already exists!";

+ 17 - 14
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-2007 Gary P. Scavone
+    Copyright (c) 2003-2009 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.7
+// RtMidi: Version 1.0.8
 
 #ifndef RTMIDI_H
 #define RTMIDI_H
@@ -48,7 +48,7 @@ class RtMidi
  public:
 
   //! Pure virtual openPort() function.
-  virtual void openPort( unsigned int portNumber = 0 ) = 0;
+  virtual void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi" ) ) = 0;
 
   //! Pure virtual openVirtualPort() function.
   virtual void openVirtualPort( const std::string portName = std::string( "RtMidi" ) ) = 0;
@@ -91,7 +91,7 @@ class RtMidi
     to open a virtual input port to which other MIDI software clients
     can connect.
 
-    by Gary P. Scavone, 2003-2004.
+    by Gary P. Scavone, 2003-2008.
 */
 /**********************************************************************/
 
@@ -105,11 +105,11 @@ class RtMidiIn : public RtMidi
   //! User callback function type definition.
   typedef void (*RtMidiCallback)( double timeStamp, std::vector<unsigned char> *message, void *userData);
 
-  //! Default constructor.
+  //! Default constructor that allows an optional client name.
   /*!
       An exception will be thrown if a MIDI system initialization error occurs.
   */
-  RtMidiIn();
+  RtMidiIn( const std::string clientName = std::string( "RtMidi Input Client") );
 
   //! If a MIDI connection is still open, it will be closed by the destructor.
   ~RtMidiIn();
@@ -119,7 +119,7 @@ class RtMidiIn : public RtMidi
       An optional port number greater than 0 can be specified.
       Otherwise, the default or first port found is opened.
   */
-  void openPort( unsigned int portNumber = 0 );
+  void openPort( unsigned int portNumber = 0, const std::string Portname = std::string( "RtMidi Input" ) );
 
   //! Create a virtual input port, with optional name, to allow software connections (OS X and ALSA only).
   /*!
@@ -200,6 +200,7 @@ class RtMidiIn : public RtMidi
   // the MIDI input handling function or thread.
   struct RtMidiInData {
     std::queue<MidiMessage> queue;
+    MidiMessage message;
     unsigned int queueLimit;
     unsigned char ignoreFlags;
     bool doInput;
@@ -208,16 +209,18 @@ class RtMidiIn : public RtMidi
     bool usingCallback;
     void *userCallback;
     void *userData;
+    bool continueSysex;
 
     // Default constructor.
     RtMidiInData()
       : queueLimit(1024), ignoreFlags(7), doInput(false), firstMessage(true),
-        apiData(0), usingCallback(false), userCallback(0), userData(0) {}
+        apiData(0), usingCallback(false), userCallback(0), userData(0),
+        continueSysex(false) {}
   };
 
  private:
 
-  void initialize( void );
+  void initialize( const std::string& clientName );
   RtMidiInData inputData_;
 
 };
@@ -232,7 +235,7 @@ class RtMidiIn : public RtMidi
     the connection.  Create multiple instances of this class to
     connect to more than one MIDI device at the same time.
 
-    by Gary P. Scavone, 2003-2004.
+    by Gary P. Scavone, 2003-2008.
 */
 /**********************************************************************/
 
@@ -240,11 +243,11 @@ class RtMidiOut : public RtMidi
 {
  public:
 
-  //! Default constructor.
+  //! Default constructor that allows an optional client name.
   /*!
       An exception will be thrown if a MIDI system initialization error occurs.
   */
-  RtMidiOut();
+  RtMidiOut( const std::string clientName = std::string( "RtMidi Output Client" ) );
 
   //! The destructor closes any open MIDI connections.
   ~RtMidiOut();
@@ -256,7 +259,7 @@ class RtMidiOut : public RtMidi
       exception is thrown if an error occurs while attempting to make
       the port connection.
   */
-  void openPort( unsigned int portNumber = 0 );
+  void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Output" ) );
 
   //! Close an open MIDI connection (if one exists).
   void closePort();
@@ -290,7 +293,7 @@ class RtMidiOut : public RtMidi
 
  private:
 
-  void initialize( void );
+  void initialize( const std::string& clientName );
 };
 
 #endif

+ 0 - 0
config.guess → config/config.guess


+ 0 - 0
config.sub → config/config.sub


+ 0 - 0
install.sh → config/install.sh


+ 21 - 16
configure.ac

@@ -1,5 +1,6 @@
 # Process this file with autoconf to produce a configure script.
 AC_INIT(RtMidi, 1.0, gary@music.mcgill.ca, rtmidi)
+AC_CONFIG_AUX_DIR(config)
 AC_CONFIG_SRCDIR(RtMidi.cpp)
 AC_CONFIG_FILES(tests/Makefile)
 
@@ -7,41 +8,46 @@ AC_CONFIG_FILES(tests/Makefile)
 AC_SUBST( GXX, ["no"] )
 
 # Checks for programs.
-AC_PROG_CC
-AC_PROG_CXX(g++ CC c++ cxx)
+AC_PROG_CXX
 
 # Checks for header files.
 AC_HEADER_STDC
-AC_CHECK_HEADERS(sys/ioctl.h unistd.h)
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_C_CONST
+#AC_CHECK_HEADERS(sys/ioctl.h unistd.h)
 
 # Check for debug
 AC_MSG_CHECKING(whether to compile debug version)
 AC_ARG_ENABLE(debug,
   [  --enable-debug = enable various debug output],
-  [AC_SUBST( debug, [-D__RTMIDI_DEBUG__] ) AC_SUBST( cflags, [-g] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)],
-  [AC_SUBST( debug, [] ) AC_SUBST( cflags, [-O2] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)])
+  [AC_SUBST( cppflag, [-D__RTMIDI_DEBUG__] ) AC_SUBST( cxxflag, [-g] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)],
+  [AC_SUBST( cppflag, [] ) AC_SUBST( cxxflag, [-O3] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)])
+
+# For -I and -D flags
+CPPFLAGS="$CPPFLAGS $cppflag"
+
+# For debugging and optimization ... overwrite default because it has both -g and -O2
+#CXXFLAGS="$CXXFLAGS $cxxflag"
+CXXFLAGS="$cxxflag"
 
 # Check compiler and use -Wall if gnu.
 if [test $GXX = "yes" ;] then
-  AC_SUBST( warn, [-Wall] )
+  AC_SUBST( cxxflag, [-Wall] )
 fi
 
+CXXFLAGS="$CXXFLAGS $cxxflag"
+
 # Checks for package options and external software
 AC_CANONICAL_HOST
 AC_MSG_CHECKING(for MIDI API)
 case $host in
   *-*-linux*)
-  AC_SUBST( midi_api, [-D__LINUX_ALSASEQ__] )
+  AC_SUBST( api, [-D__LINUX_ALSASEQ__] )
   AC_CHECK_LIB(asound, snd_seq_open, , AC_MSG_ERROR(RtMidi in Linux requires the ALSA asound library!))
   # Checks for pthread library.
   AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtMidi requires the pthread library!))
   ;;
 
   *-sgi*)
-  AC_SUBST( midi_api, ["-D__IRIX_MD__ -LANG:std -w"] )
+  AC_SUBST( api, ["-D__IRIX_MD__ -LANG:std -w"] )
   AC_MSG_RESULT(using IRIX MD)
   AC_CHECK_LIB(md, mdInit, , AC_MSG_ERROR(IRIX MIDI support requires the md library!) )
   # Checks for pthread library.
@@ -50,13 +56,13 @@ case $host in
 
   *-apple*)
   # Check for CoreAudio framework
-  AC_CHECK_HEADER(CoreMIDI/CoreMIDI.h, [AC_SUBST( midi_api, [-D__MACOSX_CORE__] )], [AC_MSG_ERROR(CoreMIDI header files not found!)] )
-  AC_SUBST( frameworks, ["-framework CoreMIDI -framework CoreFoundation -framework CoreAudio"] )
+  AC_CHECK_HEADER(CoreMIDI/CoreMIDI.h, [AC_SUBST( api, [-D__MACOSX_CORE__] )], [AC_MSG_ERROR(CoreMIDI header files not found!)] )
+  AC_SUBST( LIBS, ["-framework CoreMIDI -framework CoreFoundation -framework CoreAudio"] )
   ;;
 
   *-mingw32*)
   # Use WinMM library
-  AC_SUBST( midi_api, [-D__WINDOWS_MM__] )
+  AC_SUBST( api, [-D__WINDOWS_MM__] )
   # I can't get the following check to work so just manually add the library
   # AC_CHECK_LIB(winmm, midiInGetNumDevs, , AC_MSG_ERROR(Windows MIDI support requires the winmm library!) )
   AC_SUBST( LIBS, [-lwinmm] )
@@ -68,7 +74,6 @@ case $host in
   ;;
 esac
 
-# Checks for library functions.
-AC_PROG_GCC_TRADITIONAL
+CPPFLAGS="$CPPFLAGS $api"
 
 AC_OUTPUT

+ 44 - 7
doc/doxygen/Doxyfile

@@ -1,13 +1,14 @@
-# Doxyfile 1.3.6
+# Doxyfile 1.5.6
 
 #---------------------------------------------------------------------------
 # Project related configuration options
 #---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
 PROJECT_NAME           = RtMidi
-PROJECT_NUMBER         = 1.0.7
+PROJECT_NUMBER         = 1.0.8
 OUTPUT_DIRECTORY       = .
+CREATE_SUBDIRS         = NO
 OUTPUT_LANGUAGE        = English
-USE_WINDOWS_ENCODING   = NO
 BRIEF_MEMBER_DESC      = YES
 REPEAT_BRIEF           = YES
 ABBREVIATE_BRIEF       = 
@@ -15,17 +16,27 @@ ALWAYS_DETAILED_SEC    = NO
 INLINE_INHERITED_MEMB  = NO
 FULL_PATH_NAMES        = NO
 STRIP_FROM_PATH        = 
+STRIP_FROM_INC_PATH    = 
 SHORT_NAMES            = NO
 JAVADOC_AUTOBRIEF      = NO
+QT_AUTOBRIEF           = NO
 MULTILINE_CPP_IS_BRIEF = NO
 DETAILS_AT_TOP         = NO
 INHERIT_DOCS           = YES
-DISTRIBUTE_GROUP_DOC   = NO
+SEPARATE_MEMBER_PAGES  = NO
 TAB_SIZE               = 8
 ALIASES                = 
 OPTIMIZE_OUTPUT_FOR_C  = NO
 OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = YES
+DISTRIBUTE_GROUP_DOC   = NO
 SUBGROUPING            = YES
+TYPEDEF_HIDES_STRUCT   = NO
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
@@ -33,6 +44,8 @@ EXTRACT_ALL            = NO
 EXTRACT_PRIVATE        = NO
 EXTRACT_STATIC         = NO
 EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
 HIDE_UNDOC_MEMBERS     = YES
 HIDE_UNDOC_CLASSES     = NO
 HIDE_FRIEND_COMPOUNDS  = NO
@@ -44,6 +57,7 @@ SHOW_INCLUDE_FILES     = YES
 INLINE_INFO            = YES
 SORT_MEMBER_DOCS       = NO
 SORT_BRIEF_DOCS        = NO
+SORT_GROUP_NAMES       = NO
 SORT_BY_SCOPE_NAME     = NO
 GENERATE_TODOLIST      = YES
 GENERATE_TESTLIST      = YES
@@ -52,6 +66,10 @@ GENERATE_DEPRECATEDLIST= YES
 ENABLED_SECTIONS       = 
 MAX_INITIALIZER_LINES  = 30
 SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = NO
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = YES
+FILE_VERSION_FILTER    = 
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
@@ -59,6 +77,7 @@ QUIET                  = NO
 WARNINGS               = YES
 WARN_IF_UNDOCUMENTED   = YES
 WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
 WARN_FORMAT            = "$file:$line: $text"
 WARN_LOGFILE           = 
 #---------------------------------------------------------------------------
@@ -67,16 +86,19 @@ WARN_LOGFILE           =
 INPUT                  = tutorial.txt \
                          ../../RtMidi.h \
                          ../../RtError.h
+INPUT_ENCODING         = UTF-8
 FILE_PATTERNS          = 
 RECURSIVE              = NO
 EXCLUDE                = 
 EXCLUDE_SYMLINKS       = NO
 EXCLUDE_PATTERNS       = 
+EXCLUDE_SYMBOLS        = 
 EXAMPLE_PATH           = 
 EXAMPLE_PATTERNS       = 
 EXAMPLE_RECURSIVE      = NO
 IMAGE_PATH             = 
 INPUT_FILTER           = 
+FILTER_PATTERNS        = 
 FILTER_SOURCE_FILES    = NO
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
@@ -86,6 +108,8 @@ INLINE_SOURCES         = NO
 STRIP_CODE_COMMENTS    = YES
 REFERENCED_BY_RELATION = YES
 REFERENCES_RELATION    = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
 VERBATIM_HEADERS       = YES
 #---------------------------------------------------------------------------
 # configuration options related to the alphabetical class index
@@ -104,15 +128,21 @@ HTML_FOOTER            = footer.html
 HTML_STYLESHEET        = 
 HTML_ALIGN_MEMBERS     = YES
 GENERATE_HTMLHELP      = NO
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+HTML_DYNAMIC_SECTIONS  = NO
 CHM_FILE               = 
 HHC_LOCATION           = 
 GENERATE_CHI           = NO
+CHM_INDEX_ENCODING     = 
 BINARY_TOC             = NO
 TOC_EXPAND             = NO
 DISABLE_INDEX          = YES
 ENUM_VALUES_PER_LINE   = 4
 GENERATE_TREEVIEW      = NO
 TREEVIEW_WIDTH         = 250
+FORMULA_FONTSIZE       = 10
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -187,22 +217,29 @@ PERL_PATH              = /usr/bin/perl
 # Configuration options related to the dot tool   
 #---------------------------------------------------------------------------
 CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            = /Applications/Doxygen.app/Contents/Resources/
 HIDE_UNDOC_RELATIONS   = YES
 HAVE_DOT               = NO
+DOT_FONTNAME           = FreeSans
+DOT_FONTPATH           = 
 CLASS_GRAPH            = YES
 COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
 UML_LOOK               = NO
 TEMPLATE_RELATIONS     = NO
 INCLUDE_GRAPH          = YES
 INCLUDED_BY_GRAPH      = YES
 CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
 GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
 DOT_IMAGE_FORMAT       = png
-DOT_PATH               = 
+DOT_PATH               = /Applications/Doxygen.app/Contents/Resources/
 DOTFILE_DIRS           = 
-MAX_DOT_GRAPH_WIDTH    = 1024
-MAX_DOT_GRAPH_HEIGHT   = 1024
+DOT_GRAPH_MAX_NODES    = 50
 MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = YES
+DOT_MULTI_TARGETS      = NO
 GENERATE_LEGEND        = YES
 DOT_CLEANUP            = YES
 #---------------------------------------------------------------------------

+ 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-2007 Gary P. Scavone, McGill University. All Rights Reserved.<br>
+  <td>&copy;2003-2009 Gary P. Scavone, McGill University. All Rights Reserved.<br>
   Maintained by Gary P. Scavone, gary at music.mcgill.ca</td></tr>
 </table>
 

+ 21 - 10
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 (7 December 2007): <A href="http://music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.7.tar.gz">Version 1.0.7</A>
+Latest Release (29 January 2009): <A href="http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-1.0.8.tar.gz">Version 1.0.8</A>
 
 \section start Getting Started
 
@@ -50,7 +50,7 @@ Obviously, this example doesn't demonstrate any of the real functionality of RtM
 
 \section error Error Handling
 
-RtMidi uses a C++ exception handler called RtError, which is declared and defined in RtError.h.  The RtError class is quite simple but it does allow errors to be "caught" by RtError::Type.  Many RtMidi methods can "throw" an RtError, most typically if a driver error occurs or an invalid function argument is specified.  There are a number of cases within RtMidi where warning messages may be displayed but an exception is not thrown.  There is a protected RtMidi method, error(), which can be modified to globally control how these messages are handled and reported.  By default, error messages are not automatically displayed in RtMidi unless the preprocessor definition __RTMIDI_DEBUG__ is defined.  Messages associated with caught exceptions can be displayed with, for example, the RtError::printMessage() function.
+RtMidi uses a C++ exception handler called RtError, which is declared and defined in RtError.h.  The RtError class is quite simple but it does allow errors to be "caught" by RtError::Type.  Many RtMidi methods can "throw" an RtError, most typically if a driver error occurs or an invalid function argument is specified.  There are a number of cases within RtMidi where warning messages may be displayed but an exception is not thrown.  There is a protected RtMidi method, error(), that can be modified to globally control how these messages are handled and reported.  By default, error messages are not automatically displayed in RtMidi unless the preprocessor definition __RTMIDI_DEBUG__ is defined.  Messages associated with caught exceptions can be displayed with, for example, the RtError::printMessage() function.
 
 
 \section probing Probing Ports
@@ -61,6 +61,7 @@ A programmer may wish to query the available MIDI ports before deciding which to
 // midiprobe.cpp
 
 #include <iostream>
+#include <cstdlib>
 #include "RtMidi.h"
 
 int main()
@@ -135,6 +136,7 @@ In the following example, we omit necessary error checking and details regarding
 // midiout.cpp
 
 #include <iostream>
+#include <cstdlib>
 #include "RtMidi.h"
 
 int main()
@@ -204,6 +206,7 @@ In the following example, we omit some necessary error checking and details rega
 // qmidiin.cpp
 
 #include <iostream>
+#include <cstdlib>
 #include <signal.h>
 #include "RtMidi.h"
 
@@ -240,7 +243,7 @@ int main()
     for ( i=0; i<nBytes; i++ )
       std::cout << "Byte " << i << " = " << (int)message[i] << ", ";
     if ( nBytes > 0 )
-      std::cout << "stamp = " << stamp << '\n';
+      std::cout << "stamp = " << stamp << std::endl;
 
     // Sleep for 10 milliseconds ... platform-dependent.
     SLEEP( 10 );
@@ -256,7 +259,7 @@ int main()
 
 \subsection cmidiin MIDI Input with User Callback
 
-When set, a user-provided callback function will be invoked after the input of a complete MIDI message.  It is possible to provide a pointer to user data which can be accessed in the callback function (not shown here).  It is necessary to set the callback function immediately after opening the port to avoid having incoming messages written to the queue (which is not emptied when a callback function is set).  If you are worried about this happening, you can check the queue using the RtMidi::getMessage() function to verify it is empty (after the callback function is set).
+When set, a user-provided callback function will be invoked after the input of a complete MIDI message.  It is possible to provide a pointer to user data that can be accessed in the callback function (not shown here).  It is necessary to set the callback function immediately after opening the port to avoid having incoming messages written to the queue (which is not emptied when a callback function is set).  If you are worried about this happening, you can check the queue using the RtMidi::getMessage() function to verify it is empty (after the callback function is set).
 
 In the following example, we omit some necessary error checking.  For a more complete example, see the \c cmidiin.cpp program in the \c tests directory.
 
@@ -264,6 +267,7 @@ In the following example, we omit some necessary error checking.  For a more com
 // cmidiin.cpp
 
 #include <iostream>
+#include <cstdlib>
 #include "RtMidi.h"
 
 void mycallback( double deltatime, std::vector< unsigned char > *message, void *userData )
@@ -272,7 +276,7 @@ void mycallback( double deltatime, std::vector< unsigned char > *message, void *
   for ( unsigned int i=0; i<nBytes; i++ )
     std::cout << "Byte " << i << " = " << (int)message->at(i) << ", ";
   if ( nBytes > 0 )
-    std::cout << "stamp = " << deltatime << '\n';
+    std::cout << "stamp = " << deltatime << std::endl;
 }
 
 int main()
@@ -360,7 +364,7 @@ The example compiler statements above could be used to compile the <TT>midiprobe
 
 \section debug Debugging
 
-If you are having problems getting RtMidi to run on your system, try passing the preprocessor definition <TT>__RTMIDI_DEBUG__</TT> to the compiler (or uncomment the definition at the bottom of RtMidi.h).  A variety of warning messages will be displayed which may help in determining the problem.  Also try using the programs included in the <tt>test</tt> directory.  The program <tt>midiprobe</tt> displays the queried capabilities of all MIDI ports found.
+If you are having problems getting RtMidi to run on your system, try passing the preprocessor definition <TT>__RTMIDI_DEBUG__</TT> to the compiler (or uncomment the definition at the bottom of RtMidi.h).  A variety of warning messages will be displayed that may help in determining the problem.  Also try using the programs included in the <tt>test</tt> directory.  The program <tt>midiprobe</tt> displays the queried capabilities of all MIDI ports found.
 
 \section apinotes API Notes
 
@@ -380,23 +384,30 @@ The Irix version of RtMidi was written and tested on an SGI Indy running Irix ve
 
 \subsection windowsds Windows (Multimedia Library):
 
-The Windows Multimedia library MIDI calls used in RtMidi do not make use of streaming functionality.  RtMidi was originally developed with Visual C++ version 6.0.
+The \c configure script provides support for the MinGW compiler.
+
+The Windows Multimedia library MIDI calls used in RtMidi do not make use of streaming functionality.   Incoming system exclusive messages read by RtMidiIn are limited to a length as defined by the preprocessor definition RT_SYSEX_BUFFER_SIZE (set in RtMidi.cpp).  The default value is 1024.  There is no such limit for outgoing sysex messages via RtMidiOut.
+
+RtMidi was originally developed with Visual C++ version 6.0.
 
 The \c configure script provides support for the MinGW compiler.
 
 \section acknowledge Acknowledgements
 
-Many thanks to the following people for providing bug fixes:
+Many thanks to the following people for providing bug fixes and improvements:
 <UL>
-<LI>Pedro Lopez-Cabanillas (ALSA sequencer API)</LI>
+<LI>Pedro Lopez-Cabanillas (ALSA sequencer API, client naming)</LI>
 <LI>Eduardo Coutinho (Windows device names)</LI>
 <LI>Jean-Baptiste Berruchon (Windows sysex code)</LI>
+<LI>Christoph Eckert (ALSA sysex fixes)</LI>
+<LI>Immanuel Litzroth (OS-X sysex fix)</LI>
+<LI>Axel Schmidt (client naming)</LI>
 </UL>
 
 \section license License
 
     RtMidi: realtime MIDI i/o C++ classes<BR>
-    Copyright (c) 2003-2007 Gary P. Scavone
+    Copyright (c) 2003-2009 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-2007.
+By Gary P. Scavone, 2003-2009.
+
+v1.0.8: (29 January 2009)
+- bug fixes for concatenating segmented sysex messages in ALSA (thanks to Christoph Eckert)
+- update to ALSA sequencer port enumeration (thanks to Pedro Lopez-Cabonillas)
+- bug fixes for concatenating segmented sysex messages in OS-X (thanks to Emmanuel Litzroth)
+- added functionality for naming clients (thanks to Pedro Lopez-Cabonillas and Axel Schmidt)
+- bug fix in Windows when receiving sysex messages if the ignore flag was set (thanks to Pedro Lopez-Cabonillas)
 
 v1.0.7: (7 December 2007)
 - configure and Makefile changes for MinGW

+ 4 - 4
readme

@@ -1,6 +1,6 @@
-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.
+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-2006.
+By Gary P. Scavone, 2003-2009.
 
 This distribution of RtMidi contains the following:
 
@@ -11,7 +11,7 @@ On unix systems, type "./configure" in the top level directory, then "make" in t
 
 OVERVIEW:
 
-RtMidi is a set of C++ classes (RtMidiIn and RtMidiOut) which provide a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA), Macintosh OS X, SGI, and Windows (Multimedia Library) operating systems.  RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software.  It was designed with the following goals:
+RtMidi is a set of C++ classes (RtMidiIn and RtMidiOut) that provide a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA), Macintosh OS X, SGI, and Windows (Multimedia Library) operating systems.  RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software.  It was designed with the following goals:
 
   - object oriented C++ design
   - simple, common API across all supported platforms
@@ -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-2006 Gary P. Scavone
+    Copyright (c) 2003-2009 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files

+ 3 - 5
tests/Makefile.in

@@ -11,12 +11,10 @@ vpath %.o $(OBJECT_PATH)
 OBJECTS	=	RtMidi.o
 
 CC       = @CXX@
-DEFS     = @debug@
-DEFS    += @midi_api@
-CFLAGS   = @cflags@
-CFLAGS  += @warn@ -I$(INCLUDE)
+DEFS     = @CPPFLAGS@
+CFLAGS   = @CXXFLAGS@
+CFLAGS  += -I$(INCLUDE)
 LIBRARY  = @LIBS@
-LIBRARY += @frameworks@
 
 %.o : $(SRC_PATH)/%.cpp
 	$(CC) $(CFLAGS) $(DEFS) -c $(<) -o $(OBJECT_PATH)/$@

+ 2 - 1
tests/cmidiin.cpp

@@ -8,6 +8,7 @@
 //*****************************************//
 
 #include <iostream>
+#include <cstdlib>
 #include "RtMidi.h"
 
 void usage( void ) {
@@ -24,7 +25,7 @@ void mycallback( double deltatime, std::vector< unsigned char > *message, void *
   for ( unsigned int i=0; i<nBytes; i++ )
     std::cout << "Byte " << i << " = " << (int)message->at(i) << ", ";
   if ( nBytes > 0 )
-    std::cout << "stamp = " << deltatime << '\n';
+    std::cout << "stamp = " << deltatime << std::endl;
 }
 
 int main( int argc, char *argv[] )

+ 1 - 0
tests/midiout.cpp

@@ -7,6 +7,7 @@
 //*****************************************//
 
 #include <iostream>
+#include <cstdlib>
 #include "RtMidi.h"
 
 // Platform-dependent sleep routines.

+ 1 - 0
tests/midiprobe.cpp

@@ -5,6 +5,7 @@
 // by Gary Scavone, 2003-2004.
 
 #include <iostream>
+#include <cstdlib>
 #include "RtMidi.h"
 
 int main()

+ 2 - 1
tests/qmidiin.cpp

@@ -8,6 +8,7 @@
 //*****************************************//
 
 #include <iostream>
+#include <cstdlib>
 #include <signal.h>
 #include "RtMidi.h"
 
@@ -83,7 +84,7 @@ int main( int argc, char *argv[] )
     for ( i=0; i<nBytes; i++ )
       std::cout << "Byte " << i << " = " << (int)message[i] << ", ";
     if ( nBytes > 0 )
-      std::cout << "stamp = " << stamp << '\n';
+      std::cout << "stamp = " << stamp << std::endl;
 
     // Sleep for 10 milliseconds.
     SLEEP( 10 );

+ 3 - 1
tests/sysextest.cpp

@@ -7,6 +7,8 @@
 //*****************************************//
 
 #include <iostream>
+#include <cstdlib>
+#include <typeinfo>
 #include "RtMidi.h"
 
 void usage( void ) {
@@ -134,7 +136,7 @@ bool chooseMidiPort( RtMidi *rtmidi )
     } while ( i >= nPorts );
   }
 
-  std::cout << "\n";
+  std::cout << std::endl;
   rtmidi->openPort( i );
 
   return true;

+ 5 - 5
tests/sysextest.dsp

@@ -42,14 +42,14 @@ RSC=rc.exe
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /W3 /GX /O2 /I "../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_MM__" /YX /FD /c
+# ADD CPP /nologo /W3 /GR /GX /O2 /I "../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_MM__" /YX /FD /c
 # ADD BASE RSC /l 0x409 /d "NDEBUG"
 # ADD RSC /l 0x409 /d "NDEBUG"
 BSC32=bscmake.exe
 # ADD BASE BSC32 /nologo
 # ADD BSC32 /nologo
 LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /subsystem:console /machine:I386
 
 !ELSEIF  "$(CFG)" == "sysextest - Win32 Debug"
@@ -65,15 +65,15 @@ LINK32=link.exe
 # PROP Intermediate_Dir "Debug"
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ  /c
-# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_MM__" /YX /FD /GZ  /c
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GR /GX /ZI /Od /I "../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_MM__" /YX /FD /GZ /c
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
 # ADD RSC /l 0x409 /d "_DEBUG"
 BSC32=bscmake.exe
 # ADD BASE BSC32 /nologo
 # ADD BSC32 /nologo
 LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
 
 !ENDIF