diff --git a/source/modules/rtmidi/RtMidi.cpp b/source/modules/rtmidi/RtMidi.cpp index d0af4bf81..a3c9dcd85 100644 --- a/source/modules/rtmidi/RtMidi.cpp +++ b/source/modules/rtmidi/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-2014 Gary P. Scavone + Copyright (c) 2003-2017 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -39,8 +39,16 @@ #include "RtMidi.h" #include -// not used -#undef __UNIX_JACK__ +#if defined(__MACOSX_CORE__) + #if TARGET_OS_IPHONE + #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime + #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos + #endif +#endif + +// Default for Windows is to add an identifier to the port names; this +// flag can be defined (e.g. in your project file) to disable this behaviour. +//#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES //*********************************************************************// // RtMidi Definitions @@ -53,8 +61,7 @@ RtMidi :: RtMidi() RtMidi :: ~RtMidi() { - if ( rtapi_ ) - delete rtapi_; + delete rtapi_; rtapi_ = 0; } @@ -90,10 +97,9 @@ void RtMidi :: getCompiledApi( std::vector &apis ) throw() // RtMidiIn Definitions //*********************************************************************// -void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) +void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) { - if ( rtapi_ ) - delete rtapi_; + delete rtapi_; rtapi_ = 0; #if defined(__UNIX_JACK__) @@ -118,7 +124,7 @@ void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, uns #endif } -RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) +RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) : RtMidi() { if ( api != UNSPECIFIED ) { @@ -137,7 +143,7 @@ RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned in getCompiledApi( apis ); for ( unsigned int i=0; igetPortCount() ) break; + if ( rtapi_ && rtapi_->getPortCount() ) break; } if ( rtapi_ ) return; @@ -159,10 +165,9 @@ RtMidiIn :: ~RtMidiIn() throw() // RtMidiOut Definitions //*********************************************************************// -void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName ) +void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName ) { - if ( rtapi_ ) - delete rtapi_; + delete rtapi_; rtapi_ = 0; #if defined(__UNIX_JACK__) @@ -187,7 +192,7 @@ void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName ) #endif } -RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName ) +RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName) { if ( api != UNSPECIFIED ) { // Attempt to open the specified API. @@ -205,7 +210,7 @@ RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName ) getCompiledApi( apis ); for ( unsigned int i=0; igetPortCount() ) break; + if ( rtapi_ && rtapi_->getPortCount() ) break; } if ( rtapi_ ) return; @@ -227,7 +232,7 @@ RtMidiOut :: ~RtMidiOut() throw() //*********************************************************************// MidiApi :: MidiApi( void ) - : apiData_( 0 ), connected_( false ), errorCallback_(0) + : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0) { } @@ -235,24 +240,24 @@ MidiApi :: ~MidiApi( void ) { } -void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback ) +void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) { errorCallback_ = errorCallback; + errorCallbackUserData_ = userData; } void MidiApi :: error( RtMidiError::Type type, std::string errorString ) { if ( errorCallback_ ) { - static bool firstErrorOccured = false; - if ( firstErrorOccured ) + if ( firstErrorOccurred_ ) return; - firstErrorOccured = true; + firstErrorOccurred_ = true; const std::string errorMessage = errorString; - errorCallback_( type, errorMessage ); - firstErrorOccured = false; + errorCallback_( type, errorMessage, errorCallbackUserData_); + firstErrorOccurred_ = false; return; } @@ -339,18 +344,68 @@ double MidiInApi :: getMessage( std::vector *message ) return 0.0; } - if ( inputData_.queue.size == 0 ) return 0.0; + double timeStamp; + if (!inputData_.queue.pop(message, &timeStamp)) + return 0.0; + + return timeStamp; +} + +unsigned int MidiInApi::MidiQueue::size(unsigned int *__back, + unsigned int *__front) +{ + // Access back/front members exactly once and make stack copies for + // size calculation + unsigned int _back = back, _front = front, _size; + if (_back >= _front) + _size = _back - _front; + else + _size = ringSize - _front + _back; + + // Return copies of back/front so no new and unsynchronized accesses + // to member variables are needed. + if (__back) *__back = _back; + if (__front) *__front = _front; + return _size; +} + +// As long as we haven't reached our queue size limit, push the message. +bool MidiInApi::MidiQueue::push(const MidiInApi::MidiMessage& msg) +{ + // Local stack copies of front/back + unsigned int _back, _front, _size; + + // Get back/front indexes exactly once and calculate current size + _size = size(&_back, &_front); + + if ( _size < ringSize-1 ) + { + ring[_back] = msg; + back = (back+1)%ringSize; + return true; + } + + return false; +} + +bool MidiInApi::MidiQueue::pop(std::vector *msg, double* timeStamp) +{ + // Local stack copies of front/back + unsigned int _back, _front, _size; + + // Get back/front indexes exactly once and calculate current size + _size = size(&_back, &_front); + + if (_size == 0) + return false; // Copy queued message to the vector pointer argument and then "pop" it. - std::vector *bytes = &(inputData_.queue.ring[inputData_.queue.front].bytes); - message->assign( bytes->begin(), bytes->end() ); - double deltaTime = inputData_.queue.ring[inputData_.queue.front].timeStamp; - inputData_.queue.size--; - inputData_.queue.front++; - if ( inputData_.queue.front == inputData_.queue.ringSize ) - inputData_.queue.front = 0; + msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); + *timeStamp = ring[_front].timeStamp; - return deltaTime; + // Update front + front = (front+1)%ringSize; + return true; } //*********************************************************************// @@ -427,7 +482,6 @@ static void midiInputCallback( const MIDIPacketList *list, void *procRef, void * if ( nBytes == 0 ) continue; // Calculate time stamp. - if ( data->firstMessage ) { message.timeStamp = 0.0; data->firstMessage = false; @@ -442,11 +496,10 @@ static void midiInputCallback( const MIDIPacketList *list, void *procRef, void * if ( !continueSysex ) 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; + + // Track whether any non-filtered messages were found in this + // packet for timestamp calculation + bool foundNonFiltered = false; iByte = 0; if ( continueSysex ) { @@ -466,13 +519,7 @@ static void midiInputCallback( const MIDIPacketList *list, void *procRef, void * } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queue.size < data->queue.ringSize ) { - data->queue.ring[data->queue.back++] = message; - if ( data->queue.back == data->queue.ringSize ) - data->queue.back = 0; - data->queue.size++; - } - else + if (!data->queue.push(message)) std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; } message.bytes.clear(); @@ -521,6 +568,7 @@ static void midiInputCallback( const MIDIPacketList *list, void *procRef, void * // Copy the MIDI data to our vector. if ( size ) { + foundNonFiltered = true; message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); if ( !continueSysex ) { // If not a continuing sysex message, invoke the user callback function or queue the message. @@ -530,13 +578,7 @@ static void midiInputCallback( const MIDIPacketList *list, void *procRef, void * } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queue.size < data->queue.ringSize ) { - data->queue.ring[data->queue.back++] = message; - if ( data->queue.back == data->queue.ringSize ) - data->queue.back = 0; - data->queue.size++; - } - else + if (!data->queue.push(message)) std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; } message.bytes.clear(); @@ -545,19 +587,29 @@ static void midiInputCallback( const MIDIPacketList *list, void *procRef, void * } } } + + // Save the time of the last non-filtered message + if (foundNonFiltered) + { + apiData->lastTime = packet->timeStamp; + if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages + apiData->lastTime = AudioGetCurrentHostTime(); + } + } + packet = MIDIPacketNext(packet); } } -MidiInCore :: MidiInCore( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { - initialize( clientName ); + MidiInCore::initialize( clientName ); } MidiInCore :: ~MidiInCore( void ) { // Close a connection if it exists. - closePort(); + MidiInCore::closePort(); // Cleanup. CoreMidiData *data = static_cast (apiData_); @@ -570,9 +622,12 @@ void MidiInCore :: initialize( const std::string& clientName ) { // Set up our client. MIDIClientRef client; - OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); + CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); if ( result != noErr ) { - errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object."; + std::ostringstream ost; + ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; + errorString_ = ost.str(); error( RtMidiError::DRIVER_ERROR, errorString_ ); return; } @@ -583,9 +638,10 @@ void MidiInCore :: initialize( const std::string& clientName ) data->endpoint = 0; apiData_ = (void *) data; inputData_.apiData = (void *) data; + CFRelease(name); } -void MidiInCore :: openPort( unsigned int portNumber, const std::string portName ) +void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName ) { if ( connected_ ) { errorString_ = "MidiInCore::openPort: a valid connection already exists!"; @@ -611,9 +667,12 @@ void MidiInCore :: openPort( unsigned int portNumber, const std::string portName MIDIPortRef port; CoreMidiData *data = static_cast (apiData_); + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); OSStatus result = MIDIInputPortCreate( data->client, - CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), - midiInputCallback, (void *)&inputData_, &port ); + portNameRef, + midiInputCallback, (void *)&inputData_, &port ); + CFRelease( portNameRef ); + if ( result != noErr ) { MIDIClientDispose( data->client ); errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; @@ -647,15 +706,18 @@ void MidiInCore :: openPort( unsigned int portNumber, const std::string portName connected_ = true; } -void MidiInCore :: openVirtualPort( const std::string portName ) +void MidiInCore :: openVirtualPort( const std::string &portName ) { CoreMidiData *data = static_cast (apiData_); // Create a virtual MIDI input destination. MIDIEndpointRef endpoint; + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); OSStatus result = MIDIDestinationCreate( data->client, - CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + portNameRef, midiInputCallback, (void *)&inputData_, &endpoint ); + CFRelease( portNameRef ); + if ( result != noErr ) { errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; error( RtMidiError::DRIVER_ERROR, errorString_ ); @@ -668,11 +730,19 @@ void MidiInCore :: openVirtualPort( const std::string portName ) void MidiInCore :: closePort( void ) { - if ( connected_ ) { - CoreMidiData *data = static_cast (apiData_); + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + MIDIEndpointDispose( data->endpoint ); + data->endpoint = 0; + } + + if ( data->port ) { MIDIPortDispose( data->port ); - connected_ = false; + data->port = 0; } + + connected_ = false; } unsigned int MidiInCore :: getPortCount() @@ -801,6 +871,8 @@ static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) if ( anyStrings ) return result; + CFRelease( result ); + // Here, either the endpoint had no connections, or we failed to obtain names return EndpointName( endpoint, false ); } @@ -823,7 +895,7 @@ std::string MidiInCore :: getPortName( unsigned int portNumber ) portRef = MIDIGetSource( portNumber ); nameRef = ConnectedEndpointName(portRef); - CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding()); + CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8); CFRelease( nameRef ); return stringName = name; @@ -834,15 +906,15 @@ std::string MidiInCore :: getPortName( unsigned int portNumber ) // Class Definitions: MidiOutCore //*********************************************************************// -MidiOutCore :: MidiOutCore( const std::string clientName ) : MidiOutApi() +MidiOutCore :: MidiOutCore( const std::string &clientName ) : MidiOutApi() { - initialize( clientName ); + MidiOutCore::initialize( clientName ); } MidiOutCore :: ~MidiOutCore( void ) { // Close a connection if it exists. - closePort(); + MidiOutCore::closePort(); // Cleanup. CoreMidiData *data = static_cast (apiData_); @@ -855,9 +927,12 @@ void MidiOutCore :: initialize( const std::string& clientName ) { // Set up our client. MIDIClientRef client; - OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); + CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); if ( result != noErr ) { - errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object."; + std::ostringstream ost; + ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; + errorString_ = ost.str(); error( RtMidiError::DRIVER_ERROR, errorString_ ); return; } @@ -867,6 +942,7 @@ void MidiOutCore :: initialize( const std::string& clientName ) data->client = client; data->endpoint = 0; apiData_ = (void *) data; + CFRelease( name ); } unsigned int MidiOutCore :: getPortCount() @@ -893,13 +969,13 @@ std::string MidiOutCore :: getPortName( unsigned int portNumber ) portRef = MIDIGetDestination( portNumber ); nameRef = ConnectedEndpointName(portRef); - CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding()); + CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); CFRelease( nameRef ); return stringName = name; } -void MidiOutCore :: openPort( unsigned int portNumber, const std::string portName ) +void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName ) { if ( connected_ ) { errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; @@ -925,9 +1001,11 @@ void MidiOutCore :: openPort( unsigned int portNumber, const std::string portNam MIDIPortRef port; CoreMidiData *data = static_cast (apiData_); + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); OSStatus result = MIDIOutputPortCreate( data->client, - CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + portNameRef, &port ); + CFRelease( portNameRef ); if ( result != noErr ) { MIDIClientDispose( data->client ); errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; @@ -953,14 +1031,22 @@ void MidiOutCore :: openPort( unsigned int portNumber, const std::string portNam void MidiOutCore :: closePort( void ) { - if ( connected_ ) { - CoreMidiData *data = static_cast (apiData_); + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + MIDIEndpointDispose( data->endpoint ); + data->endpoint = 0; + } + + if ( data->port ) { MIDIPortDispose( data->port ); - connected_ = false; + data->port = 0; } + + connected_ = false; } -void MidiOutCore :: openVirtualPort( std::string portName ) +void MidiOutCore :: openVirtualPort( const std::string &portName ) { CoreMidiData *data = static_cast (apiData_); @@ -972,9 +1058,12 @@ void MidiOutCore :: openVirtualPort( std::string portName ) // Create a virtual MIDI output source. MIDIEndpointRef endpoint; + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); OSStatus result = MIDISourceCreate( data->client, - CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), + portNameRef, &endpoint ); + CFRelease( portNameRef ); + if ( result != noErr ) { errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; error( RtMidiError::DRIVER_ERROR, errorString_ ); @@ -985,76 +1074,40 @@ void MidiOutCore :: openVirtualPort( std::string portName ) data->endpoint = endpoint; } -// Not necessary if we don't treat sysex messages any differently than -// normal messages ... see below. -//static void sysexCompletionProc( MIDISysexSendRequest *sreq ) -//{ -// free( sreq ); -//} - -void MidiOutCore :: sendMessage( std::vector *message ) +void MidiOutCore :: sendMessage( const unsigned char *message, size_t size ) { // We use the MIDISendSysex() function to asynchronously send sysex // messages. Otherwise, we use a single CoreMidi MIDIPacket. - unsigned int nBytes = message->size(); + unsigned int nBytes = static_cast (size); if ( nBytes == 0 ) { errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; error( RtMidiError::WARNING, errorString_ ); return; } - // unsigned int packetBytes, bytesLeft = nBytes; - // unsigned int messageIndex = 0; MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); CoreMidiData *data = static_cast (apiData_); OSStatus result; - /* - // I don't think this code is necessary. We can send sysex - // messages through the normal mechanism. In addition, this avoids - // the problem of virtual ports not receiving sysex messages. - - if ( message->at(0) == 0xF0 ) { - - // Apple's fantastic API requires us to free the allocated data in - // the completion callback but trashes the pointer and size before - // we get a chance to free it!! This is a somewhat ugly hack - // submitted by ptarabbia that puts the sysex buffer data right at - // the end of the MIDISysexSendRequest structure. This solution - // does not require that we wait for a previous sysex buffer to be - // sent before sending a new one, which was the old way we did it. - MIDISysexSendRequest *newRequest = (MIDISysexSendRequest *) malloc(sizeof(struct MIDISysexSendRequest) + nBytes); - char * sysexBuffer = ((char *) newRequest) + sizeof(struct MIDISysexSendRequest); - - // Copy data to buffer. - for ( unsigned int i=0; iat(i); - - newRequest->destination = data->destinationId; - newRequest->data = (Byte *)sysexBuffer; - newRequest->bytesToSend = nBytes; - newRequest->complete = 0; - newRequest->completionProc = sysexCompletionProc; - newRequest->completionRefCon = newRequest; - - result = MIDISendSysex(newRequest); - if ( result != noErr ) { - free( newRequest ); - errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; - error( RtMidiError::WARNING, errorString_ ); - return; - } - return; - } - else if ( nBytes > 3 ) { + if ( message[0] != 0xF0 && nBytes > 3 ) { errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; error( RtMidiError::WARNING, errorString_ ); return; } - */ - MIDIPacketList packetList; - MIDIPacket *packet = MIDIPacketListInit( &packetList ); - packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) ); + Byte buffer[nBytes+(sizeof(MIDIPacketList))]; + ByteCount listSize = sizeof(buffer); + MIDIPacketList *packetList = (MIDIPacketList*)buffer; + MIDIPacket *packet = MIDIPacketListInit( packetList ); + + ByteCount remainingBytes = nBytes; + while (remainingBytes && packet) { + ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket + const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes]; + packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr); + remainingBytes -= bytesForPacket; + } + if ( !packet ) { errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; error( RtMidiError::DRIVER_ERROR, errorString_ ); @@ -1063,7 +1116,7 @@ void MidiOutCore :: sendMessage( std::vector *message ) // Send to any destinations that may have connected to us. if ( data->endpoint ) { - result = MIDIReceived( data->endpoint, &packetList ); + result = MIDIReceived( data->endpoint, packetList ); if ( result != noErr ) { errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; error( RtMidiError::WARNING, errorString_ ); @@ -1072,7 +1125,7 @@ void MidiOutCore :: sendMessage( std::vector *message ) // And send to an explicit destination port if we're connected. if ( connected_ ) { - result = MIDISend( data->port, data->destinationId, &packetList ); + result = MIDISend( data->port, data->destinationId, packetList ); if ( result != noErr ) { errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; error( RtMidiError::WARNING, errorString_ ); @@ -1120,7 +1173,7 @@ struct AlsaMidiData { unsigned char *buffer; pthread_t thread; pthread_t dummy_thread_id; - unsigned long long lastTime; + snd_seq_real_time_t lastTime; int queue_id; // an input queue is needed to get timestamped events int trigger_fds[2]; }; @@ -1138,7 +1191,7 @@ static void *alsaMidiHandler( void *ptr ) AlsaMidiData *apiData = static_cast (data->apiData); long nBytes; - unsigned long long time, lastTime; + double time; bool continueSysex = false; bool doDecode = false; MidiInApi::MidiMessage message; @@ -1280,14 +1333,33 @@ static void *alsaMidiHandler( void *ptr ) // 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; + + // Using method from: + // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + + // Perform the carry for the later subtraction by updating y. + snd_seq_real_time_t &x(ev->time.time); + snd_seq_real_time_t &y(apiData->lastTime); + if (x.tv_nsec < y.tv_nsec) { + int nsec = (y.tv_nsec - x.tv_nsec) / 1000000000 + 1; + y.tv_nsec -= 1000000000 * nsec; + y.tv_sec += nsec; + } + if (x.tv_nsec - y.tv_nsec > 1000000000) { + int nsec = (x.tv_nsec - y.tv_nsec) / 1000000000; + y.tv_nsec += 1000000000 * nsec; + y.tv_sec -= nsec; + } + + // Compute the time difference. + time = x.tv_sec - y.tv_sec + (x.tv_nsec - y.tv_nsec)*1e-9; + + apiData->lastTime = ev->time.time; + if ( data->firstMessage == true ) data->firstMessage = false; else - message.timeStamp = time * 0.000001; + message.timeStamp = time; } else { #if defined(__RTMIDI_DEBUG__) @@ -1306,13 +1378,7 @@ static void *alsaMidiHandler( void *ptr ) } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queue.size < data->queue.ringSize ) { - data->queue.ring[data->queue.back++] = message; - if ( data->queue.back == data->queue.ringSize ) - data->queue.back = 0; - data->queue.size++; - } - else + if (!data->queue.push(message)) std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; } } @@ -1324,15 +1390,15 @@ static void *alsaMidiHandler( void *ptr ) return 0; } -MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { - initialize( clientName ); + MidiInAlsa::initialize( clientName ); } MidiInAlsa :: ~MidiInAlsa() { // Close a connection if it exists. - closePort(); + MidiInAlsa::closePort(); // Shutdown the input thread. AlsaMidiData *data = static_cast (apiData_); @@ -1418,7 +1484,10 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int snd_seq_port_info_set_port( pinfo, -1 ); while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { unsigned int atyp = snd_seq_port_info_get_type( pinfo ); - if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue; + if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) && + ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) && + ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue; + unsigned int caps = snd_seq_port_info_get_capability( pinfo ); if ( ( caps & type ) != type ) continue; if ( count == portNumber ) return 1; @@ -1454,6 +1523,8 @@ std::string MidiInAlsa :: getPortName( unsigned int portNumber ) 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_name( pinfo ); os << " "; // These lines added to make sure devices are listed os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names os << ":"; @@ -1468,7 +1539,7 @@ std::string MidiInAlsa :: getPortName( unsigned int portNumber ) return stringName; } -void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName ) +void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName ) { if ( connected_ ) { errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; @@ -1497,6 +1568,7 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName snd_seq_addr_t sender, receiver; sender.client = snd_seq_port_info_get_client( src_pinfo ); sender.port = snd_seq_port_info_get_port( src_pinfo ); + receiver.client = snd_seq_client_id( data->seq ); snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); @@ -1526,7 +1598,6 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName data->vport = snd_seq_port_info_get_port(pinfo); } - receiver.client = snd_seq_port_info_get_client( pinfo ); receiver.port = data->vport; if ( !data->subscription ) { @@ -1576,7 +1647,7 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName connected_ = true; } -void MidiInAlsa :: openVirtualPort( std::string portName ) +void MidiInAlsa :: openVirtualPort( const std::string &portName ) { AlsaMidiData *data = static_cast (apiData_); if ( data->vport < 0 ) { @@ -1671,15 +1742,15 @@ void MidiInAlsa :: closePort( void ) // Class Definitions: MidiOutAlsa //*********************************************************************// -MidiOutAlsa :: MidiOutAlsa( const std::string clientName ) : MidiOutApi() +MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi() { - initialize( clientName ); + MidiOutAlsa::initialize( clientName ); } MidiOutAlsa :: ~MidiOutAlsa() { // Close a connection if it exists. - closePort(); + MidiOutAlsa::closePort(); // Cleanup. AlsaMidiData *data = static_cast (apiData_); @@ -1753,6 +1824,8 @@ std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) 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_name( pinfo ); os << " "; // These lines added to make sure devices are listed os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names os << ":"; @@ -1767,7 +1840,7 @@ std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) return stringName; } -void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string portName ) +void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName ) { if ( connected_ ) { errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; @@ -1838,11 +1911,12 @@ void MidiOutAlsa :: closePort( void ) AlsaMidiData *data = static_cast (apiData_); snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; connected_ = false; } } -void MidiOutAlsa :: openVirtualPort( std::string portName ) +void MidiOutAlsa :: openVirtualPort( const std::string &portName ) { AlsaMidiData *data = static_cast (apiData_); if ( data->vport < 0 ) { @@ -1857,11 +1931,11 @@ void MidiOutAlsa :: openVirtualPort( std::string portName ) } } -void MidiOutAlsa :: sendMessage( std::vector *message ) +void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size ) { int result; AlsaMidiData *data = static_cast (apiData_); - unsigned int nBytes = message->size(); + unsigned int nBytes = static_cast (size); if ( nBytes > data->bufferSize ) { data->bufferSize = nBytes; result = snd_midi_event_resize_buffer ( data->coder, nBytes); @@ -1884,7 +1958,7 @@ void MidiOutAlsa :: sendMessage( std::vector *message ) snd_seq_ev_set_source(&ev, data->vport); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); - for ( unsigned int i=0; ibuffer[i] = message->at(i); + for ( unsigned int i=0; ibuffer[i] = message[i]; result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); if ( result < (int)nBytes ) { errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; @@ -1924,6 +1998,34 @@ void MidiOutAlsa :: sendMessage( std::vector *message ) #include #include +// Convert a null-terminated wide string or ANSI-encoded string to UTF-8. +static std::string ConvertToUTF8(const TCHAR *str) +{ + std::string u8str; + const WCHAR *wstr = L""; +#if defined( UNICODE ) || defined( _UNICODE ) + wstr = str; +#else + // Convert from ANSI encoding to wide string + int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + std::wstring wstrtemp; + if ( wlength ) + { + wstrtemp.assign( wlength - 1, 0 ); + MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength ); + wstr = &wstrtemp[0]; + } +#endif + // Convert from wide string to UTF-8 + int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL ); + if ( length ) + { + u8str.assign( length - 1, 0 ); + length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL ); + } + return u8str; +} + #define RT_SYSEX_BUFFER_SIZE 1024 #define RT_SYSEX_BUFFER_COUNT 4 @@ -1961,7 +2063,6 @@ static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, data->firstMessage = false; } else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; - apiData->lastTime = timestamp; if ( inputStatus == MIM_DATA ) { // Channel or system message @@ -2022,35 +2123,32 @@ static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, else return; } + // Save the time of the last non-filtered message + apiData->lastTime = timestamp; + if ( data->usingCallback ) { RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queue.size < data->queue.ringSize ) { - data->queue.ring[data->queue.back++] = apiData->message; - if ( data->queue.back == data->queue.ringSize ) - data->queue.back = 0; - data->queue.size++; - } - else - std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + if (!data->queue.push(apiData->message)) + std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n"; } // Clear the vector for the next input message. apiData->message.bytes.clear(); } -MidiInWinMM :: MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { - initialize( clientName ); + MidiInWinMM::initialize( clientName ); } MidiInWinMM :: ~MidiInWinMM() { // Close a connection if it exists. - closePort(); + MidiInWinMM::closePort(); WinMidiData *data = static_cast (apiData_); DeleteCriticalSection( &(data->_mutex) ); @@ -2081,7 +2179,7 @@ void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) } } -void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ ) +void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) { if ( connected_ ) { errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; @@ -2127,6 +2225,7 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portN result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); + data->inHandle = 0; errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; error( RtMidiError::DRIVER_ERROR, errorString_ ); return; @@ -2136,6 +2235,7 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portN result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); + data->inHandle = 0; errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; error( RtMidiError::DRIVER_ERROR, errorString_ ); return; @@ -2145,6 +2245,7 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portN result = midiInStart( data->inHandle ); if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); + data->inHandle = 0; errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; error( RtMidiError::DRIVER_ERROR, errorString_ ); return; @@ -2153,7 +2254,7 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portN connected_ = true; } -void MidiInWinMM :: openVirtualPort( std::string /*portName*/ ) +void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ ) { // This function cannot be implemented for the Windows MM MIDI API. errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; @@ -2174,6 +2275,7 @@ void MidiInWinMM :: closePort( void ) delete [] data->sysexBuffer[i]; if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); + data->inHandle = 0; errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; error( RtMidiError::DRIVER_ERROR, errorString_ ); return; @@ -2181,6 +2283,7 @@ void MidiInWinMM :: closePort( void ) } midiInClose( data->inHandle ); + data->inHandle = 0; connected_ = false; LeaveCriticalSection( &(data->_mutex) ); } @@ -2205,22 +2308,17 @@ std::string MidiInWinMM :: getPortName( unsigned int portNumber ) MIDIINCAPS deviceCaps; midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); - -#if defined( UNICODE ) || defined( _UNICODE ) - int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1; - stringName.assign( length, 0 ); - length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL); -#else - stringName = std::string( deviceCaps.szPname ); -#endif + stringName = ConvertToUTF8( deviceCaps.szPname ); // Next lines added to add the portNumber to the name so that // the device's names are sure to be listed with individual names // even when they have the same brand name +#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES std::ostringstream os; os << " "; os << portNumber; stringName += os.str(); +#endif return stringName; } @@ -2230,15 +2328,15 @@ std::string MidiInWinMM :: getPortName( unsigned int portNumber ) // Class Definitions: MidiOutWinMM //*********************************************************************// -MidiOutWinMM :: MidiOutWinMM( const std::string clientName ) : MidiOutApi() +MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi() { - initialize( clientName ); + MidiOutWinMM::initialize( clientName ); } MidiOutWinMM :: ~MidiOutWinMM() { // Close a connection if it exists. - closePort(); + MidiOutWinMM::closePort(); // Cleanup. WinMidiData *data = static_cast (apiData_); @@ -2279,19 +2377,22 @@ std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) MIDIOUTCAPS deviceCaps; midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); + stringName = ConvertToUTF8( deviceCaps.szPname ); -#if defined( UNICODE ) || defined( _UNICODE ) - int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1; - stringName.assign( length, 0 ); - length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL); -#else - stringName = std::string( deviceCaps.szPname ); + // Next lines added to add the portNumber to the name so that + // the device's names are sure to be listed with individual names + // even when they have the same brand name + std::ostringstream os; +#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES + os << " "; + os << portNumber; + stringName += os.str(); #endif return stringName; } -void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ ) +void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) { if ( connected_ ) { errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; @@ -2335,22 +2436,23 @@ void MidiOutWinMM :: closePort( void ) WinMidiData *data = static_cast (apiData_); midiOutReset( data->outHandle ); midiOutClose( data->outHandle ); + data->outHandle = 0; connected_ = false; } } -void MidiOutWinMM :: openVirtualPort( std::string /*portName*/ ) +void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ ) { // This function cannot be implemented for the Windows MM MIDI API. errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; error( RtMidiError::WARNING, errorString_ ); } -void MidiOutWinMM :: sendMessage( std::vector *message ) +void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size ) { if ( !connected_ ) return; - unsigned int nBytes = static_cast(message->size()); + unsigned int nBytes = static_cast(size); if ( nBytes == 0 ) { errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; error( RtMidiError::WARNING, errorString_ ); @@ -2359,7 +2461,7 @@ void MidiOutWinMM :: sendMessage( std::vector *message ) MMRESULT result; WinMidiData *data = static_cast (apiData_); - if ( message->at(0) == 0xF0 ) { // Sysex message + if ( message[0] == 0xF0 ) { // Sysex message // Allocate buffer for sysex data. char *buffer = (char *) malloc( nBytes ); @@ -2370,7 +2472,7 @@ void MidiOutWinMM :: sendMessage( std::vector *message ) } // Copy data to buffer. - for ( unsigned int i=0; iat(i); + for ( unsigned int i=0; i *message ) DWORD packet; unsigned char *ptr = (unsigned char *) &packet; for ( unsigned int i=0; iat(i); + *ptr = message[i]; ++ptr; } @@ -2441,6 +2543,9 @@ void MidiOutWinMM :: sendMessage( std::vector *message ) #include #include #include +#ifdef HAVE_SEMAPHORE + #include +#endif #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer @@ -2450,6 +2555,10 @@ struct JackMidiData { jack_ringbuffer_t *buffSize; jack_ringbuffer_t *buffMessage; jack_time_t lastTime; +#ifdef HAVE_SEMAPHORE + sem_t sem_cleanup; + sem_t sem_needpost; +#endif MidiInApi :: RtMidiInData *rtMidiIn; }; @@ -2496,13 +2605,7 @@ static int jackProcessIn( jack_nframes_t nframes, void *arg ) } else { // As long as we haven't reached our queue size limit, push the message. - if ( rtData->queue.size < rtData->queue.ringSize ) { - rtData->queue.ring[rtData->queue.back++] = message; - if ( rtData->queue.back == rtData->queue.ringSize ) - rtData->queue.back = 0; - rtData->queue.size++; - } - else + if (!rtData->queue.push(message)) std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; } } @@ -2511,9 +2614,9 @@ static int jackProcessIn( jack_nframes_t nframes, void *arg ) return 0; } -MidiInJack :: MidiInJack( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { - initialize( clientName ); + MidiInJack::initialize( clientName ); } void MidiInJack :: initialize( const std::string& clientName ) @@ -2549,14 +2652,14 @@ void MidiInJack :: connect() MidiInJack :: ~MidiInJack() { JackMidiData *data = static_cast (apiData_); - closePort(); + MidiInJack::closePort(); if ( data->client ) jack_client_close( data->client ); delete data; } -void MidiInJack :: openPort( unsigned int portNumber, const std::string portName ) +void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName ) { JackMidiData *data = static_cast (apiData_); @@ -2578,7 +2681,7 @@ void MidiInJack :: openPort( unsigned int portNumber, const std::string portName jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); } -void MidiInJack :: openVirtualPort( const std::string portName ) +void MidiInJack :: openVirtualPort( const std::string &portName ) { JackMidiData *data = static_cast (apiData_); @@ -2677,12 +2780,17 @@ static int jackProcessOut( jack_nframes_t nframes, void *arg ) jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); } +#ifdef HAVE_SEMAPHORE + if (!sem_trywait(&data->sem_needpost)) + sem_post(&data->sem_cleanup); +#endif + return 0; } -MidiOutJack :: MidiOutJack( const std::string clientName ) : MidiOutApi() +MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi() { - initialize( clientName ); + MidiOutJack::initialize( clientName ); } void MidiOutJack :: initialize( const std::string& clientName ) @@ -2692,6 +2800,10 @@ void MidiOutJack :: initialize( const std::string& clientName ) data->port = NULL; data->client = NULL; +#ifdef HAVE_SEMAPHORE + sem_init(&data->sem_cleanup, 0, 0); + sem_init(&data->sem_needpost, 0, 0); +#endif this->clientName = clientName; connect(); @@ -2702,6 +2814,10 @@ void MidiOutJack :: connect() JackMidiData *data = static_cast (apiData_); if ( data->client ) return; + + // Initialize output ringbuffers + data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); + data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); // Initialize JACK client if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { @@ -2711,27 +2827,30 @@ void MidiOutJack :: connect() } jack_set_process_callback( data->client, jackProcessOut, data ); - data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); - data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); jack_activate( data->client ); } MidiOutJack :: ~MidiOutJack() { JackMidiData *data = static_cast (apiData_); - closePort(); + MidiOutJack::closePort(); + // Cleanup + jack_ringbuffer_free( data->buffSize ); + jack_ringbuffer_free( data->buffMessage ); if ( data->client ) { - // Cleanup jack_client_close( data->client ); - jack_ringbuffer_free( data->buffSize ); - jack_ringbuffer_free( data->buffMessage ); } +#ifdef HAVE_SEMAPHORE + sem_destroy(&data->sem_cleanup); + sem_destroy(&data->sem_needpost); +#endif + delete data; } -void MidiOutJack :: openPort( unsigned int portNumber, const std::string portName ) +void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName ) { JackMidiData *data = static_cast (apiData_); @@ -2753,7 +2872,7 @@ void MidiOutJack :: openPort( unsigned int portNumber, const std::string portNam jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); } -void MidiOutJack :: openVirtualPort( const std::string portName ) +void MidiOutJack :: openVirtualPort( const std::string &portName ) { JackMidiData *data = static_cast (apiData_); @@ -2824,18 +2943,29 @@ void MidiOutJack :: closePort() JackMidiData *data = static_cast (apiData_); if ( data->port == NULL ) return; + +#ifdef HAVE_SEMAPHORE + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) != -1) + { + ts.tv_sec += 1; // wait max one second + sem_post(&data->sem_needpost); + sem_timedwait(&data->sem_cleanup, &ts); + } +#endif + jack_port_unregister( data->client, data->port ); data->port = NULL; } -void MidiOutJack :: sendMessage( std::vector *message ) +void MidiOutJack :: sendMessage( const unsigned char *message, size_t size ) { - int nBytes = message->size(); + int nBytes = static_cast(size); JackMidiData *data = static_cast (apiData_); // Write full message to buffer - jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0], - message->size() ); + jack_ringbuffer_write( data->buffMessage, ( const char * ) message, + nBytes ); jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); } diff --git a/source/modules/rtmidi/RtMidi.h b/source/modules/rtmidi/RtMidi.h index 9a8788849..0ace4c3fb 100644 --- a/source/modules/rtmidi/RtMidi.h +++ b/source/modules/rtmidi/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-2014 Gary P. Scavone + Copyright (c) 2003-2017 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -43,7 +43,8 @@ #ifndef RTMIDI_H #define RTMIDI_H -#define RTMIDI_VERSION "2.1.0" +#define RTMIDI_DLL_PUBLIC +#define RTMIDI_VERSION "3.0.0" #include #include @@ -60,7 +61,7 @@ */ /************************************************************************/ -class RtMidiError : public std::exception +class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception { public: //! Defined RtMidiError types. @@ -109,11 +110,11 @@ class RtMidiError : public std::exception Note that class behaviour is undefined after a critical error (not a warning) is reported. */ -typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText ); +typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData ); class MidiApi; -class RtMidi +class RTMIDI_DLL_PUBLIC RtMidi { public: @@ -139,10 +140,10 @@ class RtMidi static void getCompiledApi( std::vector &apis ) throw(); //! Pure virtual openPort() function. - virtual void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi" ) ) = 0; + virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0; //! Pure virtual openVirtualPort() function. - virtual void openVirtualPort( const std::string portName = std::string( "RtMidi" ) ) = 0; + virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0; //! Pure virtual getPortCount() function. virtual unsigned int getPortCount() = 0; @@ -154,6 +155,10 @@ class RtMidi virtual void closePort( void ) = 0; //! Returns true if a port is open and false if not. + /*! + Note that this only applies to connections made with the openPort() + function, not to virtual ports. + */ virtual bool isPortOpen( void ) const = 0; //! Set an error callback function to be invoked when an error has occured. @@ -161,7 +166,7 @@ class RtMidi The callback function will be called whenever an error has occured. It is best to set the error callback function before opening a port. */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL ) = 0; + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0; protected: @@ -185,7 +190,7 @@ class RtMidi possible to open a virtual input port to which other MIDI software clients can connect. - by Gary P. Scavone, 2003-2014. + by Gary P. Scavone, 2003-2017. */ /**********************************************************************/ @@ -203,7 +208,7 @@ class RtMidi // // **************************************************************** // -class RtMidiIn : public RtMidi +class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi { public: @@ -229,7 +234,7 @@ class RtMidiIn : public RtMidi \param queueSizeLimit An optional size of the MIDI input queue can be specified. */ RtMidiIn( RtMidi::Api api=UNSPECIFIED, - const std::string clientName = std::string( "RtMidi Input Client"), + const std::string& clientName = "RtMidi Input Client", unsigned int queueSizeLimit = 100 ); //! If a MIDI connection is still open, it will be closed by the destructor. @@ -244,7 +249,7 @@ class RtMidiIn : public RtMidi Otherwise, the default or first port found is opened. \param portName An optional name for the application port that is used to connect to portId can be specified. */ - void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Input" ) ); + void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) ); //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only). /*! @@ -256,7 +261,7 @@ class RtMidiIn : public RtMidi \param portName An optional name for the application port that is used to connect to portId can be specified. */ - void openVirtualPort( const std::string portName = std::string( "RtMidi Input" ) ); + void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) ); //! Set a callback function to be invoked for incoming MIDI messages. /*! @@ -282,6 +287,10 @@ class RtMidiIn : public RtMidi void closePort( void ); //! Returns true if a port is open and false if not. + /*! + Note that this only applies to connections made with the openPort() + function, not to virtual ports. + */ virtual bool isPortOpen() const; //! Return the number of available MIDI input ports. @@ -293,7 +302,8 @@ class RtMidiIn : public RtMidi //! Return a string identifier for the specified MIDI input port number. /*! \return The name of the port with the given Id is returned. - \retval An empty string is returned if an invalid port specifier is provided. + \retval An empty string is returned if an invalid port specifier + is provided. User code should assume a UTF-8 encoding. */ std::string getPortName( unsigned int portNumber = 0 ); @@ -322,10 +332,10 @@ class RtMidiIn : public RtMidi The callback function will be called whenever an error has occured. It is best to set the error callback function before opening a port. */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL ); + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); protected: - void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ); + void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ); }; @@ -341,11 +351,11 @@ class RtMidiIn : public RtMidi OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a virtual port to which other MIDI software clients can connect. - by Gary P. Scavone, 2003-2014. + by Gary P. Scavone, 2003-2017. */ /**********************************************************************/ -class RtMidiOut : public RtMidi +class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi { public: @@ -358,7 +368,7 @@ class RtMidiOut : public RtMidi JACK (OS-X). */ RtMidiOut( RtMidi::Api api=UNSPECIFIED, - const std::string clientName = std::string( "RtMidi Output Client") ); + const std::string& clientName = "RtMidi Output Client" ); //! The destructor closes any open MIDI connections. ~RtMidiOut( void ) throw(); @@ -373,12 +383,16 @@ class RtMidiOut : public RtMidi exception is thrown if an error occurs while attempting to make the port connection. */ - void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Output" ) ); + void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) ); //! Close an open MIDI connection (if one exists). void closePort( void ); //! Returns true if a port is open and false if not. + /*! + Note that this only applies to connections made with the openPort() + function, not to virtual ports. + */ virtual bool isPortOpen() const; //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only). @@ -390,14 +404,16 @@ class RtMidiOut : public RtMidi An exception is thrown if an error occurs while attempting to create the virtual port. */ - void openVirtualPort( const std::string portName = std::string( "RtMidi Output" ) ); + void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) ); //! Return the number of available MIDI output ports. unsigned int getPortCount( void ); //! Return a string identifier for the specified MIDI port type and number. /*! - An empty string is returned if an invalid port specifier is provided. + \return The name of the port with the given Id is returned. + \retval An empty string is returned if an invalid port specifier + is provided. User code should assume a UTF-8 encoding. */ std::string getPortName( unsigned int portNumber = 0 ); @@ -406,17 +422,27 @@ class RtMidiOut : public RtMidi An exception is thrown if an error occurs during output or an output connection was not previously established. */ - void sendMessage( std::vector *message ); + void sendMessage( const std::vector *message ); + + //! Immediately send a single message out an open MIDI output port. + /*! + An exception is thrown if an error occurs during output or an + output connection was not previously established. + + \param message A pointer to the MIDI message as raw bytes + \param size Length of the MIDI message in bytes + */ + void sendMessage( const unsigned char *message, size_t size ); //! Set an error callback function to be invoked when an error has occured. /*! The callback function will be called whenever an error has occured. It is best to set the error callback function before opening a port. */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL ); + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); protected: - void openMidiApi( RtMidi::Api api, const std::string clientName ); + void openMidiApi( RtMidi::Api api, const std::string &clientName ); }; @@ -433,22 +459,22 @@ class RtMidiOut : public RtMidi // // **************************************************************** // -class MidiApi +class RTMIDI_DLL_PUBLIC MidiApi { public: MidiApi(); virtual ~MidiApi(); virtual RtMidi::Api getCurrentApi( void ) = 0; - virtual void openPort( unsigned int portNumber, const std::string portName ) = 0; - virtual void openVirtualPort( const std::string portName ) = 0; + virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0; + virtual void openVirtualPort( const std::string &portName ) = 0; virtual void closePort( void ) = 0; virtual unsigned int getPortCount( void ) = 0; virtual std::string getPortName( unsigned int portNumber ) = 0; inline bool isPortOpen() const { return connected_; } - void setErrorCallback( RtMidiErrorCallback errorCallback ); + void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ); //! A basic error reporting function for RtMidi classes. void error( RtMidiError::Type type, std::string errorString ); @@ -460,9 +486,11 @@ protected: bool connected_; std::string errorString_; RtMidiErrorCallback errorCallback_; + bool firstErrorOccurred_; + void *errorCallbackUserData_; }; -class MidiInApi : public MidiApi +class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi { public: @@ -477,6 +505,8 @@ class MidiInApi : public MidiApi // messages. Each message represents one and only one MIDI message. struct MidiMessage { std::vector bytes; + + //! Time in seconds elapsed since the previous message double timeStamp; // Default constructor. @@ -487,13 +517,16 @@ class MidiInApi : public MidiApi struct MidiQueue { unsigned int front; unsigned int back; - unsigned int size; unsigned int ringSize; MidiMessage *ring; // Default constructor. MidiQueue() - :front(0), back(0), size(0), ringSize(0) {} + :front(0), back(0), ringSize(0), ring(0) {} + bool push(const MidiMessage&); + bool pop(std::vector*, double*); + unsigned int size(unsigned int *back=0, + unsigned int *front=0); }; // The RtMidiInData structure is used to pass private class data to @@ -521,13 +554,13 @@ class MidiInApi : public MidiApi RtMidiInData inputData_; }; -class MidiOutApi : public MidiApi +class RTMIDI_DLL_PUBLIC MidiOutApi : public MidiApi { public: MidiOutApi( void ); virtual ~MidiOutApi( void ); - virtual void sendMessage( std::vector *message ) = 0; + virtual void sendMessage( const unsigned char *message, size_t size ) = 0; }; // **************************************************************** // @@ -537,8 +570,8 @@ class MidiOutApi : public MidiApi // **************************************************************** // inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } -inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); } -inline void RtMidiIn :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); } +inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } +inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); } inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { ((MidiInApi *)rtapi_)->setCallback( callback, userData ); } @@ -547,17 +580,18 @@ inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCou inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } inline double RtMidiIn :: getMessage( std::vector *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); } -inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback ) { rtapi_->setErrorCallback(errorCallback); } +inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } -inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); } -inline void RtMidiOut :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); } +inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } +inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } -inline void RtMidiOut :: sendMessage( std::vector *message ) { ((MidiOutApi *)rtapi_)->sendMessage( message ); } -inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback ) { rtapi_->setErrorCallback(errorCallback); } +inline void RtMidiOut :: sendMessage( const std::vector *message ) { ((MidiOutApi *)rtapi_)->sendMessage( &message->at(0), message->size() ); } +inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { ((MidiOutApi *)rtapi_)->sendMessage( message, size ); } +inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } // **************************************************************** // // @@ -574,11 +608,11 @@ inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback ) { class MidiInCore: public MidiInApi { public: - MidiInCore( const std::string clientName, unsigned int queueSizeLimit ); + MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInCore( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); @@ -590,15 +624,15 @@ class MidiInCore: public MidiInApi class MidiOutCore: public MidiOutApi { public: - MidiOutCore( const std::string clientName ); + MidiOutCore( const std::string &clientName ); ~MidiOutCore( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); - void sendMessage( std::vector *message ); + void sendMessage( const unsigned char *message, size_t size ); protected: void initialize( const std::string& clientName ); @@ -611,11 +645,11 @@ class MidiOutCore: public MidiOutApi class MidiInJack: public MidiInApi { public: - MidiInJack( const std::string clientName, unsigned int queueSizeLimit ); + MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInJack( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); @@ -630,15 +664,15 @@ class MidiInJack: public MidiInApi class MidiOutJack: public MidiOutApi { public: - MidiOutJack( const std::string clientName ); + MidiOutJack( const std::string &clientName ); ~MidiOutJack( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); - void sendMessage( std::vector *message ); + void sendMessage( const unsigned char *message, size_t size ); protected: std::string clientName; @@ -654,11 +688,11 @@ class MidiOutJack: public MidiOutApi class MidiInAlsa: public MidiInApi { public: - MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ); + MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInAlsa( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); @@ -670,15 +704,15 @@ class MidiInAlsa: public MidiInApi class MidiOutAlsa: public MidiOutApi { public: - MidiOutAlsa( const std::string clientName ); + MidiOutAlsa( const std::string &clientName ); ~MidiOutAlsa( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); - void sendMessage( std::vector *message ); + void sendMessage( const unsigned char *message, size_t size ); protected: void initialize( const std::string& clientName ); @@ -691,11 +725,11 @@ class MidiOutAlsa: public MidiOutApi class MidiInWinMM: public MidiInApi { public: - MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ); + MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ); ~MidiInWinMM( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); @@ -707,15 +741,15 @@ class MidiInWinMM: public MidiInApi class MidiOutWinMM: public MidiOutApi { public: - MidiOutWinMM( const std::string clientName ); + MidiOutWinMM( const std::string &clientName ); ~MidiOutWinMM( void ); RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; - void openPort( unsigned int portNumber, const std::string portName ); - void openVirtualPort( const std::string portName ); + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); void closePort( void ); unsigned int getPortCount( void ); std::string getPortName( unsigned int portNumber ); - void sendMessage( std::vector *message ); + void sendMessage( const unsigned char *message, size_t size ); protected: void initialize( const std::string& clientName ); @@ -728,13 +762,13 @@ class MidiOutWinMM: public MidiOutApi class MidiInDummy: public MidiInApi { public: - MidiInDummy( const std::string /*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } + MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } - void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {} - void openVirtualPort( const std::string /*portName*/ ) {} + void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} + void openVirtualPort( const std::string &/*portName*/ ) {} void closePort( void ) {} unsigned int getPortCount( void ) { return 0; } - std::string getPortName( unsigned int portNumber ) { return ""; } + std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } protected: void initialize( const std::string& /*clientName*/ ) {} @@ -743,14 +777,14 @@ class MidiInDummy: public MidiInApi class MidiOutDummy: public MidiOutApi { public: - MidiOutDummy( const std::string /*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } + MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } - void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {} - void openVirtualPort( const std::string /*portName*/ ) {} + void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} + void openVirtualPort( const std::string &/*portName*/ ) {} void closePort( void ) {} unsigned int getPortCount( void ) { return 0; } std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } - void sendMessage( std::vector * /*message*/ ) {} + void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {} protected: void initialize( const std::string& /*clientName*/ ) {}