Mercurial > hg > svcore
diff data/midi/rtmidi/RtMidi.cpp @ 565:3086876472dc
* rtmidi & activity updates
author | Chris Cannam |
---|---|
date | Tue, 24 Feb 2009 17:25:55 +0000 |
parents | a5e0d1068cae |
children | 1a30913e26bc |
line wrap: on
line diff
--- a/data/midi/rtmidi/RtMidi.cpp Tue Feb 24 11:39:45 2009 +0000 +++ b/data/midi/rtmidi/RtMidi.cpp Tue Feb 24 17:25:55 2009 +0000 @@ -8,7 +8,7 @@ RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2007 Gary P. Scavone + Copyright (c) 2003-2009 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -35,7 +35,7 @@ */ /**********************************************************************/ -// RtMidi: Version 1.0.7 +// RtMidi: Version 1.0.8 #include "RtMidi.h" #include <sstream> @@ -69,9 +69,9 @@ // Common RtMidiIn Definitions //*********************************************************************// -RtMidiIn :: RtMidiIn(std::string name) : RtMidi() +RtMidiIn :: RtMidiIn( const std::string clientName ) : RtMidi() { - this->initialize(name); + this->initialize( clientName ); } void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) @@ -144,9 +144,9 @@ // Common RtMidiOut Definitions //*********************************************************************// -RtMidiOut :: RtMidiOut(std::string name) : RtMidi() +RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi() { - this->initialize(name); + this->initialize( clientName ); } @@ -187,20 +187,24 @@ RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (procRef); CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData); - bool continueSysex = false; unsigned char status; unsigned short nBytes, iByte, size; unsigned long long time; - RtMidiIn::MidiMessage message; + + bool& continueSysex = data->continueSysex; + RtMidiIn::MidiMessage& message = data->message; + const MIDIPacket *packet = &list->packet[0]; for ( unsigned int i=0; i<list->numPackets; ++i ) { // My interpretation of the CoreMIDI documentation: all message // types, except sysex, are complete within a packet and there may // be several of them in a single packet. Sysex messages can be - // broken across multiple packets but are bundled alone within a - // packet. I'm assuming that sysex messages, if segmented, must - // be complete within the same MIDIPacketList. + // broken across multiple packets and PacketLists but are bundled + // alone within each packet (these packets do not contain other + // message types). If sysex messages are split across multiple + // MIDIPacketLists, they must be handled by multiple calls to this + // function. nBytes = packet->length; if ( nBytes == 0 ) continue; @@ -220,12 +224,13 @@ iByte = 0; if ( continueSysex ) { // We have a continuing, segmented sysex message. - if ( !(data->ignoreFlags & 0x01) ) { + if ( !( data->ignoreFlags & 0x01 ) ) { // If we're not ignoring sysex messages, copy the entire packet. for ( unsigned int j=0; j<nBytes; j++ ) message.bytes.push_back( packet->data[j] ); } - if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; + continueSysex = packet->data[nBytes-1] != 0xF7; + if ( !continueSysex ) { // If not a continuing sysex message, invoke the user callback function or queue the message. if ( data->usingCallback && message.bytes.size() > 0 ) { @@ -259,7 +264,7 @@ iByte = nBytes; } else size = nBytes - iByte; - if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; + continueSysex = packet->data[nBytes-1] != 0xF7; } else if ( status < 0xF3 ) { if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { @@ -311,13 +316,11 @@ } } -void RtMidiIn :: initialize(std::string name) +void RtMidiIn :: initialize( const std::string& clientName ) { // Set up our client. MIDIClientRef client; - OSStatus result = MIDIClientCreate - ( CFStringCreateWithBytes(0, (unsigned char *)name.data(), - name.size(), kCFStringEncodingUTF8, 0), NULL, NULL, &client ); + OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); if ( result != noErr ) { errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object."; error( RtError::DRIVER_ERROR ); @@ -331,7 +334,7 @@ inputData_.apiData = (void *) data; } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -354,7 +357,9 @@ MIDIPortRef port; CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); - OSStatus result = MIDIInputPortCreate( data->client, CFSTR("MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port ); + OSStatus result = MIDIInputPortCreate( data->client, + CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + midiInputCallback, (void *)&inputData_, &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; @@ -481,13 +486,11 @@ return stringName; } -void RtMidiOut :: initialize(std::string name) +void RtMidiOut :: initialize( const std::string& clientName ) { // Set up our client. MIDIClientRef client; - OSStatus result = MIDIClientCreate - ( CFStringCreateWithBytes(0, (unsigned char *)name.data(), - name.size(), kCFStringEncodingUTF8, 0), NULL, NULL, &client ); + OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); if ( result != noErr ) { errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object."; error( RtError::DRIVER_ERROR ); @@ -500,7 +503,7 @@ apiData_ = (void *) data; } -void RtMidiOut :: openPort( unsigned int portNumber ) +void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; @@ -523,7 +526,9 @@ MIDIPortRef port; CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); - OSStatus result = MIDIOutputPortCreate( data->client, CFSTR("Virtual MIDI Output Port"), &port ); + OSStatus result = MIDIOutputPortCreate( data->client, + CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; @@ -592,33 +597,59 @@ void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) { + // The CoreMidi documentation indicates a maximum PackList size of + // 64K, so we may need to break long sysex messages into pieces and + // send via separate lists. unsigned int nBytes = message->size(); - // Pad the buffer for extra (unknown) structure data. - Byte buffer[nBytes+32]; - MIDIPacketList *pktlist = (MIDIPacketList *) buffer; - MIDIPacket *curPacket = MIDIPacketListInit( pktlist ); + if ( nBytes == 0 ) { + errorString_ = "RtMidiOut::sendMessage: no data in message argument!"; + error( RtError::WARNING ); + return; + } + if ( nBytes > 3 && ( message->at(0) != 0xF0 ) ) { + errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?"; + error( RtError::WARNING ); + return; + } + + unsigned int packetBytes, bytesLeft = nBytes; + unsigned int messageIndex = 0; MIDITimeStamp timeStamp = 0; - curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) ); - CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); - // Send to any destinations that may have connected to us. - OSStatus result; - if ( data->endpoint ) { - result = MIDIReceived( data->endpoint, pktlist ); - if ( result != noErr ) { - errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations."; - error( RtError::WARNING ); + while ( bytesLeft > 0 ) { + + packetBytes = ( bytesLeft > 32736 ) ? 32736 : bytesLeft; + Byte buffer[packetBytes + 32]; // extra memory for other structure variables + MIDIPacketList *packetList = (MIDIPacketList *) buffer; + MIDIPacket *curPacket = MIDIPacketListInit( packetList ); + + curPacket = MIDIPacketListAdd( packetList, packetBytes+32, curPacket, timeStamp, packetBytes, (const Byte *) &message->at( messageIndex ) ); + if ( !curPacket ) { + errorString_ = "RtMidiOut::sendMessage: could not allocate packet list"; + error( RtError::DRIVER_ERROR ); } - } + messageIndex += packetBytes; + bytesLeft -= packetBytes; - // And send to an explicit destination port if we're connected. - if ( connected_ ) { - result = MIDISend( data->port, data->destinationId, pktlist ); - if ( result != noErr ) { - errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; - error( RtError::WARNING ); + // Send to any destinations that may have connected to us. + OSStatus result; + if ( data->endpoint ) { + result = MIDIReceived( data->endpoint, packetList ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations."; + error( RtError::WARNING ); + } + } + + // And send to an explicit destination port if we're connected. + if ( connected_ ) { + result = MIDISend( data->port, data->destinationId, packetList ); + if ( result != noErr ) { + errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; + error( RtError::WARNING ); + } } } } @@ -717,7 +748,9 @@ // This is a bit weird, but we now have to decode an ALSA MIDI // event (back) into MIDI bytes. We'll ignore non-MIDI types. - message.bytes.clear(); + if ( !continueSysex ) + message.bytes.clear(); + switch ( ev->type ) { case SND_SEQ_EVENT_PORT_SUBSCRIBED: @@ -727,8 +760,15 @@ break; case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: +#if defined(__RTMIDI_DEBUG__) std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; - data->doInput = false; + // FIXME: this is called for all unsubscribe events, even ones + //not related to this particular connection. As it stands, I + //see no data provided in the "source" and "dest" fields so + //there is nothing we can do about this at this time. + // std::cout << "sender = " << ev->source.client << ", dest = " << ev->dest.port << std::endl; +#endif + //data->doInput = false; break; case SND_SEQ_EVENT_QFRAME: // MIDI time code @@ -772,7 +812,7 @@ else message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); - continueSysex = ( ev->type == SND_SEQ_EVENT_SYSEX && message.bytes.back() != 0xF7 ); + continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); if ( continueSysex ) break; @@ -798,7 +838,7 @@ snd_seq_free_event(ev); if ( message.bytes.size() == 0 ) continue; - if ( data->usingCallback ) { + if ( data->usingCallback && !continueSysex ) { RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; callback( message.timeStamp, &message.bytes, data->userData ); } @@ -817,10 +857,10 @@ return 0; } -void RtMidiIn :: initialize(std::string name) +void RtMidiIn :: initialize( const std::string& clientName ) { // Set up the ALSA sequencer client. - snd_seq_t *seq; + snd_seq_t *seq; int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); if ( result < 0 ) { errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; @@ -828,7 +868,7 @@ } // Set client name. - snd_seq_set_client_name(seq, name.c_str()); + snd_seq_set_client_name( seq, clientName.c_str() ); // Save our api-specific connection information. AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; @@ -864,7 +904,10 @@ snd_seq_port_info_set_client( pinfo, client ); snd_seq_port_info_set_port( pinfo, -1 ); while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { - if ( !PORT_TYPE( pinfo, type ) ) continue; + unsigned int atyp = snd_seq_port_info_get_type( pinfo ); + if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue; + unsigned int caps = snd_seq_port_info_get_capability( pinfo ); + if ( ( caps & type ) != type ) continue; if ( count == portNumber ) return 1; count++; } @@ -875,7 +918,7 @@ return 0; } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -889,8 +932,8 @@ error( RtError::NO_DEVICES_FOUND ); } - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); std::ostringstream ost; AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { @@ -899,8 +942,6 @@ error( RtError::INVALID_PARAMETER ); } - char name[20]; - sprintf(name, "Input %d", portNumber + 1); snd_seq_addr_t sender, receiver; sender.client = snd_seq_port_info_get_client( pinfo ); @@ -919,7 +960,7 @@ snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); - snd_seq_port_info_set_name(pinfo, name); + snd_seq_port_info_set_name(pinfo, portName.c_str() ); data->vport = snd_seq_create_port(data->seq, pinfo); if ( data->vport < 0 ) { @@ -1055,12 +1096,20 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber ) { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_client_info_alloca( &cinfo ); + snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { - std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); + int cnum = snd_seq_port_info_get_client( pinfo ); + snd_seq_get_any_client_info( data->seq, cnum, cinfo ); + std::ostringstream os; + os << snd_seq_client_info_get_name( cinfo ); + os << ":"; + os << snd_seq_port_info_get_port( pinfo ); + std::string stringName = os.str(); return stringName; } @@ -1086,12 +1135,20 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber ) { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_client_info_alloca( &cinfo ); + snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { - std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); + int cnum = snd_seq_port_info_get_client(pinfo); + snd_seq_get_any_client_info( data->seq, cnum, cinfo ); + std::ostringstream os; + os << snd_seq_client_info_get_name(cinfo); + os << ":"; + os << snd_seq_port_info_get_port(pinfo); + std::string stringName = os.str(); return stringName; } @@ -1101,18 +1158,18 @@ return 0; } -void RtMidiOut :: initialize(std::string name) +void RtMidiOut :: initialize( const std::string& clientName ) { // Set up the ALSA sequencer client. - snd_seq_t *seq; - int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0); + snd_seq_t *seq; + int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); if ( result < 0 ) { errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object."; error( RtError::DRIVER_ERROR ); } // Set client name. - snd_seq_set_client_name(seq, name.c_str()); + snd_seq_set_client_name( seq, clientName.c_str() ); // Save our api-specific connection information. AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; @@ -1137,7 +1194,7 @@ apiData_ = (void *) data; } -void RtMidiOut :: openPort( unsigned int portNumber ) +void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; @@ -1151,8 +1208,8 @@ error( RtError::NO_DEVICES_FOUND ); } - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); std::ostringstream ost; AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { @@ -1166,11 +1223,8 @@ receiver.port = snd_seq_port_info_get_port( pinfo ); sender.client = snd_seq_client_id( data->seq ); - char name[20]; - sprintf(name, "Output %d", portNumber + 1); - if ( data->vport < 0 ) { - data->vport = snd_seq_create_simple_port( data->seq, name, + data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC ); if ( data->vport < 0 ) { @@ -1432,7 +1486,7 @@ return 0; } -void RtMidiIn :: initialize(std::string name) +void RtMidiIn :: initialize( const std::string& /*clientName*/ ) { // Initialize the Irix MIDI system. At the moment, we will not // worry about a return value of zero (ports) because there is a @@ -1445,7 +1499,7 @@ inputData_.apiData = (void *) data; } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -1573,7 +1627,7 @@ return stringName; } -void RtMidiOut :: initialize(std::string name) +void RtMidiOut :: initialize( const std::string& /*clientName*/ ) { // Initialize the Irix MIDI system. At the moment, we will not // worry about a return value of zero (ports) because there is a @@ -1585,7 +1639,7 @@ apiData_ = (void *) data; } -void RtMidiOut :: openPort( unsigned int portNumber ) +void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; @@ -1763,21 +1817,31 @@ unsigned char *ptr = (unsigned char *) &midiMessage; for ( int i=0; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ ); } - else if ( !(data->ignoreFlags & 0x01) ) { - // Sysex message and we're not ignoring it - MIDIHDR *sysex = ( MIDIHDR *) midiMessage; - for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) - apiData->message.bytes.push_back( sysex->lpData[i] ); + else { // Sysex message ( MIM_LONGDATA ) + MIDIHDR *sysex = ( MIDIHDR *) midiMessage; + if ( !( data->ignoreFlags & 0x01 ) ) { + // Sysex message and we're not ignoring it + for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) + apiData->message.bytes.push_back( sysex->lpData[i] ); + } - // When the callback has to be unaffected (application closes), - // it seems WinMM calls it with an empty sysex to de-queue the buffer - // If the buffer is requeued afer that message, the PC suddenly reboots - // after one or two minutes (JB). + // The WinMM API requires that the sysex buffer be requeued after + // input of each sysex message. Even if we are ignoring sysex + // messages, we still need to requeue the buffer in case the user + // decides to not ignore sysex messages in the future. However, + // it seems that WinMM calls this function with an empty sysex + // buffer when an application closes and in this case, we should + // avoid requeueing it, else the computer suddenly reboots after + // one or two minutes. if ( apiData->sysexBuffer->dwBytesRecorded > 0 ) { + //if ( sysex->dwBytesRecorded > 0 ) { MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; + + if ( data->ignoreFlags & 0x01 ) return; } + else return; } if ( data->usingCallback ) { @@ -1792,13 +1856,11 @@ std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; } - // Clear the vector for the next input message. Note that doing - // this here allows our code to work for sysex messages which are - // segmented across multiple buffers. + // Clear the vector for the next input message. apiData->message.bytes.clear(); } -void RtMidiIn :: initialize(std::string name) +void RtMidiIn :: initialize( const std::string& /*clientName*/ ) { // We'll issue a warning here if no devices are available but not // throw an error since the user can plugin something later. @@ -1815,7 +1877,7 @@ data->message.bytes.clear(); // needs to be empty for first input message } -void RtMidiIn :: openPort( unsigned int portNumber ) +void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; @@ -1849,8 +1911,8 @@ // Allocate and init the sysex buffer. data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; - data->sysexBuffer->lpData = new char[1024]; - data->sysexBuffer->dwBufferLength = 1024; + data->sysexBuffer->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; + data->sysexBuffer->dwBufferLength = RT_SYSEX_BUFFER_SIZE; data->sysexBuffer->dwFlags = 0; result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) ); @@ -1979,7 +2041,7 @@ return stringName; } -void RtMidiOut :: initialize(std::string name) +void RtMidiOut :: initialize( const std::string& /*clientName*/ ) { // We'll issue a warning here if no devices are available but not // throw an error since the user can plug something in later. @@ -1994,7 +2056,7 @@ apiData_ = (void *) data; } -void RtMidiOut :: openPort( unsigned int portNumber ) +void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!";