summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Scavone <gary@music.mcgill.ca>2013-10-11 14:49:11 +0200
committerStephen Sinclair <sinclair@music.mcgill.ca>2013-10-11 14:49:11 +0200
commit8fec3d71d441efbada609d2139ddf8ff6aa7b914 (patch)
tree760e2e74be6be3f4c81bc35b9e8a5d932e0338d8
parent94ef7a9c9202314c5fe9de2dc24b16ded6b7d98c (diff)
Version 1.0.12
-rw-r--r--RtMidi.cpp336
-rw-r--r--RtMidi.h8
-rw-r--r--doc/doxygen/Doxyfile123
-rw-r--r--doc/doxygen/footer.html2
-rw-r--r--doc/doxygen/tutorial.txt15
-rw-r--r--doc/release.txt9
-rw-r--r--msw/readme1
-rwxr-xr-xmsw/rtmidilib.sln20
-rwxr-xr-xmsw/rtmidilib.vcproj178
-rw-r--r--readme4
-rw-r--r--tests/Makefile.in9
-rw-r--r--tests/cmidiin.cpp93
-rw-r--r--tests/midiout.cpp18
-rw-r--r--tests/midiprobe.cpp62
14 files changed, 616 insertions, 262 deletions
diff --git a/RtMidi.cpp b/RtMidi.cpp
index de8b6ae..9f66208 100644
--- a/RtMidi.cpp
+++ b/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 );
diff --git a/RtMidi.h b/RtMidi.h
index 1e3b731..be7156e 100644
--- a/RtMidi.h
+++ b/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 );
diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile
index 103f24e..bf946e3 100644
--- a/doc/doxygen/Doxyfile
+++ b/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
diff --git a/doc/doxygen/footer.html b/doc/doxygen/footer.html
index dc3d30f..54a1b7d 100644
--- a/doc/doxygen/footer.html
+++ b/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>
diff --git a/doc/doxygen/tutorial.txt b/doc/doxygen/tutorial.txt
index 4984589..28ba5b8 100644
--- a/doc/doxygen/tutorial.txt
+++ b/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
diff --git a/doc/release.txt b/doc/release.txt
index abfe577..93667b9 100644
--- a/doc/release.txt
+++ b/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)
diff --git a/msw/readme b/msw/readme
new file mode 100644
index 0000000..e692d6d
--- /dev/null
+++ b/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. \ No newline at end of file
diff --git a/msw/rtmidilib.sln b/msw/rtmidilib.sln
new file mode 100755
index 0000000..7302ae5
--- /dev/null
+++ b/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
diff --git a/msw/rtmidilib.vcproj b/msw/rtmidilib.vcproj
new file mode 100755
index 0000000..af1f75c
--- /dev/null
+++ b/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>
diff --git a/readme b/readme
index cf26d46..1b1946c 100644
--- a/readme
+++ b/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
diff --git a/tests/Makefile.in b/tests/Makefile.in
index f33d669..f8966bf 100644
--- a/tests/Makefile.in
+++ b/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)
diff --git a/tests/cmidiin.cpp b/tests/cmidiin.cpp
index 0556b80..f2f392b 100644
--- a/tests/cmidiin.cpp
+++ b/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;
+}
diff --git a/tests/midiout.cpp b/tests/midiout.cpp
index ed65b0d..4da65c1 100644
--- a/tests/midiout.cpp
+++ b/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;
diff --git a/tests/midiprobe.cpp b/tests/midiprobe.cpp
index 06d9497..e9ba757 100644
--- a/tests/midiprobe.cpp
+++ b/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;