diff options
| author | Gary Scavone <gary@music.mcgill.ca> | 2013-10-11 14:49:11 +0200 |
|---|---|---|
| committer | Stephen Sinclair <sinclair@music.mcgill.ca> | 2013-10-11 14:49:11 +0200 |
| commit | 8fec3d71d441efbada609d2139ddf8ff6aa7b914 (patch) | |
| tree | 760e2e74be6be3f4c81bc35b9e8a5d932e0338d8 | |
| parent | 94ef7a9c9202314c5fe9de2dc24b16ded6b7d98c (diff) | |
Version 1.0.12
| -rw-r--r-- | RtMidi.cpp | 336 | ||||
| -rw-r--r-- | RtMidi.h | 8 | ||||
| -rw-r--r-- | doc/doxygen/Doxyfile | 123 | ||||
| -rw-r--r-- | doc/doxygen/footer.html | 2 | ||||
| -rw-r--r-- | doc/doxygen/tutorial.txt | 15 | ||||
| -rw-r--r-- | doc/release.txt | 9 | ||||
| -rw-r--r-- | msw/readme | 1 | ||||
| -rwxr-xr-x | msw/rtmidilib.sln | 20 | ||||
| -rwxr-xr-x | msw/rtmidilib.vcproj | 178 | ||||
| -rw-r--r-- | readme | 4 | ||||
| -rw-r--r-- | tests/Makefile.in | 9 | ||||
| -rw-r--r-- | tests/cmidiin.cpp | 93 | ||||
| -rw-r--r-- | tests/midiout.cpp | 18 | ||||
| -rw-r--r-- | tests/midiprobe.cpp | 62 |
14 files changed, 616 insertions, 262 deletions
@@ -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 ); @@ -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>©2003-2010 Gary P. Scavone, McGill University. All Rights Reserved.<br>
+ <td>©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>
@@ -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; |
