Merge pull request #110 from tesselode/fixes
[wdl/wdl-ol.git] / WDL / rtaudiomidi / RtMidi.cpp
blob5efd81d714dead3f77cd54c471f7861ab5aa5a18
1 /**********************************************************************/
2 /*! \class RtMidi
3 \brief An abstract base class for realtime MIDI input/output.
5 This class implements some common functionality for the realtime
6 MIDI input/output subclasses RtMidiIn and RtMidiOut.
8 RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
10 RtMidi: realtime MIDI i/o C++ classes
11 Copyright (c) 2003-2011 Gary P. Scavone
13 Permission is hereby granted, free of charge, to any person
14 obtaining a copy of this software and associated documentation files
15 (the "Software"), to deal in the Software without restriction,
16 including without limitation the rights to use, copy, modify, merge,
17 publish, distribute, sublicense, and/or sell copies of the Software,
18 and to permit persons to whom the Software is furnished to do so,
19 subject to the following conditions:
21 The above copyright notice and this permission notice shall be
22 included in all copies or substantial portions of the Software.
24 Any person wishing to distribute modifications to the Software is
25 requested to send the modifications to the original developer so that
26 they can be incorporated into the canonical version.
28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
31 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
32 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
33 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 /**********************************************************************/
38 // RtMidi: Version 1.0.14
40 #include "RtMidi.h"
41 #include <sstream>
43 //*********************************************************************//
44 // Common RtMidi Definitions
45 //*********************************************************************//
47 RtMidi :: RtMidi()
48 : apiData_( 0 ), connected_( false )
52 void RtMidi :: error( RtError::Type type )
54 if (type == RtError::WARNING) {
55 std::cerr << '\n' << errorString_ << "\n\n";
57 else if (type == RtError::DEBUG_WARNING) {
58 #if defined(__RTMIDI_DEBUG__)
59 std::cerr << '\n' << errorString_ << "\n\n";
60 #endif
62 else {
63 std::cerr << '\n' << errorString_ << "\n\n";
64 throw RtError( errorString_, type );
68 //*********************************************************************//
69 // Common RtMidiIn Definitions
70 //*********************************************************************//
72 RtMidiIn :: RtMidiIn( const std::string clientName ) : RtMidi()
74 this->initialize( clientName );
77 void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData )
79 if ( inputData_.usingCallback ) {
80 errorString_ = "RtMidiIn::setCallback: a callback function is already set!";
81 error( RtError::WARNING );
82 return;
85 if ( !callback ) {
86 errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";
87 error( RtError::WARNING );
88 return;
91 inputData_.userCallback = (void *) callback;
92 inputData_.userData = userData;
93 inputData_.usingCallback = true;
96 void RtMidiIn :: cancelCallback()
98 if ( !inputData_.usingCallback ) {
99 errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";
100 error( RtError::WARNING );
101 return;
104 inputData_.userCallback = 0;
105 inputData_.userData = 0;
106 inputData_.usingCallback = false;
109 void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize )
111 inputData_.queueLimit = queueSize;
114 void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )
116 inputData_.ignoreFlags = 0;
117 if ( midiSysex ) inputData_.ignoreFlags = 0x01;
118 if ( midiTime ) inputData_.ignoreFlags |= 0x02;
119 if ( midiSense ) inputData_.ignoreFlags |= 0x04;
122 double RtMidiIn :: getMessage( std::vector<unsigned char> *message )
124 message->clear();
126 if ( inputData_.usingCallback ) {
127 errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";
128 error( RtError::WARNING );
129 return 0.0;
132 if ( inputData_.queue.size() == 0 ) return 0.0;
134 // Copy queued message to the vector pointer argument and then "pop" it.
135 std::vector<unsigned char> *bytes = &(inputData_.queue.front().bytes);
136 message->assign( bytes->begin(), bytes->end() );
137 double deltaTime = inputData_.queue.front().timeStamp;
138 inputData_.queue.pop();
140 return deltaTime;
143 //*********************************************************************//
144 // Common RtMidiOut Definitions
145 //*********************************************************************//
147 RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi()
149 this->initialize( clientName );
153 //*********************************************************************//
154 // API: Macintosh OS-X
155 //*********************************************************************//
157 // API information found at:
158 // - http://developer.apple.com/audio/pdf/coreaudio.pdf
160 #if defined(__MACOSX_CORE__)
162 // The CoreMIDI API is based on the use of a callback function for
163 // MIDI input. We convert the system specific time stamps to delta
164 // time values.
166 // OS-X CoreMIDI header files.
167 #include <CoreMIDI/CoreMIDI.h>
168 #include <CoreAudio/HostTime.h>
169 #include <CoreServices/CoreServices.h>
171 // A structure to hold variables related to the CoreMIDI API
172 // implementation.
173 struct CoreMidiData {
174 MIDIClientRef client;
175 MIDIPortRef port;
176 MIDIEndpointRef endpoint;
177 MIDIEndpointRef destinationId;
178 unsigned long long lastTime;
179 MIDISysexSendRequest sysexreq;
182 //*********************************************************************//
183 // API: OS-X
184 // Class Definitions: RtMidiIn
185 //*********************************************************************//
187 void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef )
189 RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (procRef);
190 CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
192 unsigned char status;
193 unsigned short nBytes, iByte, size;
194 unsigned long long time;
196 bool& continueSysex = data->continueSysex;
197 RtMidiIn::MidiMessage& message = data->message;
199 const MIDIPacket *packet = &list->packet[0];
200 for ( unsigned int i=0; i<list->numPackets; ++i ) {
202 // My interpretation of the CoreMIDI documentation: all message
203 // types, except sysex, are complete within a packet and there may
204 // be several of them in a single packet. Sysex messages can be
205 // broken across multiple packets and PacketLists but are bundled
206 // alone within each packet (these packets do not contain other
207 // message types). If sysex messages are split across multiple
208 // MIDIPacketLists, they must be handled by multiple calls to this
209 // function.
211 nBytes = packet->length;
212 if ( nBytes == 0 ) continue;
214 // Calculate time stamp.
215 message.timeStamp = 0.0;
216 if ( data->firstMessage )
217 data->firstMessage = false;
218 else {
219 time = packet->timeStamp;
220 if ( time == 0 ) { // this happens when receiving asynchronous sysex messages
221 time = AudioGetCurrentHostTime();
223 time -= apiData->lastTime;
224 time = AudioConvertHostTimeToNanos( time );
225 message.timeStamp = time * 0.000000001;
227 apiData->lastTime = packet->timeStamp;
228 if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages
229 apiData->lastTime = AudioGetCurrentHostTime();
231 //std::cout << "TimeStamp = " << packet->timeStamp << std::endl;
233 iByte = 0;
234 if ( continueSysex ) {
235 // We have a continuing, segmented sysex message.
236 if ( !( data->ignoreFlags & 0x01 ) ) {
237 // If we're not ignoring sysex messages, copy the entire packet.
238 for ( unsigned int j=0; j<nBytes; ++j )
239 message.bytes.push_back( packet->data[j] );
241 continueSysex = packet->data[nBytes-1] != 0xF7;
243 if ( !continueSysex ) {
244 // If not a continuing sysex message, invoke the user callback function or queue the message.
245 if ( data->usingCallback && message.bytes.size() > 0 ) {
246 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
247 callback( message.timeStamp, &message.bytes, data->userData );
249 else {
250 // As long as we haven't reached our queue size limit, push the message.
251 if ( data->queueLimit > data->queue.size() )
252 data->queue.push( message );
253 else
254 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
256 message.bytes.clear();
259 else {
260 while ( iByte < nBytes ) {
261 size = 0;
262 // We are expecting that the next byte in the packet is a status byte.
263 status = packet->data[iByte];
264 if ( !(status & 0x80) ) break;
265 // Determine the number of bytes in the MIDI message.
266 if ( status < 0xC0 ) size = 3;
267 else if ( status < 0xE0 ) size = 2;
268 else if ( status < 0xF0 ) size = 3;
269 else if ( status == 0xF0 ) {
270 // A MIDI sysex
271 if ( data->ignoreFlags & 0x01 ) {
272 size = 0;
273 iByte = nBytes;
275 else size = nBytes - iByte;
276 continueSysex = packet->data[nBytes-1] != 0xF7;
278 else if ( status == 0xF1 ) {
279 // A MIDI time code message
280 if ( data->ignoreFlags & 0x02 ) {
281 size = 0;
282 iByte += 2;
284 else size = 2;
286 else if ( status == 0xF2 ) size = 3;
287 else if ( status == 0xF3 ) size = 2;
288 else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {
289 // A MIDI timing tick message and we're ignoring it.
290 size = 0;
291 iByte += 1;
293 else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {
294 // A MIDI active sensing message and we're ignoring it.
295 size = 0;
296 iByte += 1;
298 else size = 1;
300 // Copy the MIDI data to our vector.
301 if ( size ) {
302 message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );
303 if ( !continueSysex ) {
304 // If not a continuing sysex message, invoke the user callback function or queue the message.
305 if ( data->usingCallback ) {
306 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
307 callback( message.timeStamp, &message.bytes, data->userData );
309 else {
310 // As long as we haven't reached our queue size limit, push the message.
311 if ( data->queueLimit > data->queue.size() )
312 data->queue.push( message );
313 else
314 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
316 message.bytes.clear();
318 iByte += size;
322 packet = MIDIPacketNext(packet);
326 void RtMidiIn :: initialize( const std::string& clientName )
328 // Set up our client.
329 MIDIClientRef client;
330 OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
331 if ( result != noErr ) {
332 errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object.";
333 error( RtError::DRIVER_ERROR );
336 // Save our api-specific connection information.
337 CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
338 data->client = client;
339 data->endpoint = 0;
340 apiData_ = (void *) data;
341 inputData_.apiData = (void *) data;
344 void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
346 if ( connected_ ) {
347 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
348 error( RtError::WARNING );
349 return;
352 unsigned int nSrc = MIDIGetNumberOfSources();
353 if (nSrc < 1) {
354 errorString_ = "RtMidiIn::openPort: no MIDI input sources found!";
355 error( RtError::NO_DEVICES_FOUND );
358 std::ostringstream ost;
359 if ( portNumber >= nSrc ) {
360 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
361 errorString_ = ost.str();
362 error( RtError::INVALID_PARAMETER );
365 MIDIPortRef port;
366 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
367 OSStatus result = MIDIInputPortCreate( data->client,
368 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
369 midiInputCallback, (void *)&inputData_, &port );
370 if ( result != noErr ) {
371 MIDIClientDispose( data->client );
372 errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port.";
373 error( RtError::DRIVER_ERROR );
376 // Get the desired input source identifier.
377 MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
378 if ( endpoint == 0 ) {
379 MIDIPortDispose( port );
380 MIDIClientDispose( data->client );
381 errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference.";
382 error( RtError::DRIVER_ERROR );
385 // Make the connection.
386 result = MIDIPortConnectSource( port, endpoint, NULL );
387 if ( result != noErr ) {
388 MIDIPortDispose( port );
389 MIDIClientDispose( data->client );
390 errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port.";
391 error( RtError::DRIVER_ERROR );
394 // Save our api-specific port information.
395 data->port = port;
397 connected_ = true;
400 void RtMidiIn :: openVirtualPort( const std::string portName )
402 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
404 // Create a virtual MIDI input destination.
405 MIDIEndpointRef endpoint;
406 OSStatus result = MIDIDestinationCreate( data->client,
407 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
408 midiInputCallback, (void *)&inputData_, &endpoint );
409 if ( result != noErr ) {
410 errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination.";
411 error( RtError::DRIVER_ERROR );
414 // Save our api-specific connection information.
415 data->endpoint = endpoint;
418 void RtMidiIn :: closePort( void )
420 if ( connected_ ) {
421 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
422 MIDIPortDispose( data->port );
423 connected_ = false;
427 RtMidiIn :: ~RtMidiIn()
429 // Close a connection if it exists.
430 closePort();
432 // Cleanup.
433 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
434 MIDIClientDispose( data->client );
435 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
436 delete data;
439 unsigned int RtMidiIn :: getPortCount()
441 return MIDIGetNumberOfSources();
444 // This function was submitted by Douglas Casey Tucker and apparently
445 // derived largely from PortMidi.
446 CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
448 CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
449 CFStringRef str;
451 // Begin with the endpoint's name.
452 str = NULL;
453 MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );
454 if ( str != NULL ) {
455 CFStringAppend( result, str );
456 CFRelease( str );
459 MIDIEntityRef entity = NULL;
460 MIDIEndpointGetEntity( endpoint, &entity );
461 if ( entity == 0 )
462 // probably virtual
463 return result;
465 if ( CFStringGetLength( result ) == 0 ) {
466 // endpoint name has zero length -- try the entity
467 str = NULL;
468 MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );
469 if ( str != NULL ) {
470 CFStringAppend( result, str );
471 CFRelease( str );
474 // now consider the device's name
475 MIDIDeviceRef device = 0;
476 MIDIEntityGetDevice( entity, &device );
477 if ( device == 0 )
478 return result;
480 str = NULL;
481 MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );
482 if ( CFStringGetLength( result ) == 0 ) {
483 CFRelease( result );
484 return str;
486 if ( str != NULL ) {
487 // if an external device has only one entity, throw away
488 // the endpoint name and just use the device name
489 if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {
490 CFRelease( result );
491 return str;
492 } else {
493 if ( CFStringGetLength( str ) == 0 ) {
494 CFRelease( str );
495 return result;
497 // does the entity name already start with the device name?
498 // (some drivers do this though they shouldn't)
499 // if so, do not prepend
500 if ( CFStringCompareWithOptions( result, /* endpoint name */
501 str /* device name */,
502 CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {
503 // prepend the device name to the entity name
504 if ( CFStringGetLength( result ) > 0 )
505 CFStringInsert( result, 0, CFSTR(" ") );
506 CFStringInsert( result, 0, str );
508 CFRelease( str );
511 return result;
514 // This function was submitted by Douglas Casey Tucker and apparently
515 // derived largely from PortMidi.
516 static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )
518 CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
519 CFStringRef str;
520 OSStatus err;
521 int i;
523 // Does the endpoint have connections?
524 CFDataRef connections = NULL;
525 int nConnected = 0;
526 bool anyStrings = false;
527 err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );
528 if ( connections != NULL ) {
529 // It has connections, follow them
530 // Concatenate the names of all connected devices
531 nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);
532 if ( nConnected ) {
533 const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
534 for ( i=0; i<nConnected; ++i, ++pid ) {
535 MIDIUniqueID id = EndianS32_BtoN( *pid );
536 MIDIObjectRef connObject;
537 MIDIObjectType connObjectType;
538 err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );
539 if ( err == noErr ) {
540 if ( connObjectType == kMIDIObjectType_ExternalSource ||
541 connObjectType == kMIDIObjectType_ExternalDestination ) {
542 // Connected to an external device's endpoint (10.3 and later).
543 str = EndpointName( (MIDIEndpointRef)(connObject), true );
544 } else {
545 // Connected to an external device (10.2) (or something else, catch-
546 str = NULL;
547 MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );
549 if ( str != NULL ) {
550 if ( anyStrings )
551 CFStringAppend( result, CFSTR(", ") );
552 else anyStrings = true;
553 CFStringAppend( result, str );
554 CFRelease( str );
559 CFRelease( connections );
561 if ( anyStrings )
562 return result;
564 // Here, either the endpoint had no connections, or we failed to obtain names
565 return EndpointName( endpoint, false );
568 std::string RtMidiIn :: getPortName( unsigned int portNumber )
570 CFStringRef nameRef;
571 MIDIEndpointRef portRef;
572 std::ostringstream ost;
573 char name[128];
575 std::string stringName;
576 if ( portNumber >= MIDIGetNumberOfSources() ) {
577 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
578 errorString_ = ost.str();
579 error( RtError::WARNING );
580 //error( RtError::INVALID_PARAMETER );
581 return stringName;
584 portRef = MIDIGetSource( portNumber );
585 nameRef = ConnectedEndpointName(portRef);
586 CFStringGetCString( nameRef, name, sizeof(name), 0);
587 CFRelease( nameRef );
589 return stringName = name;
592 //*********************************************************************//
593 // API: OS-X
594 // Class Definitions: RtMidiOut
595 //*********************************************************************//
597 unsigned int RtMidiOut :: getPortCount()
599 return MIDIGetNumberOfDestinations();
602 std::string RtMidiOut :: getPortName( unsigned int portNumber )
604 CFStringRef nameRef;
605 MIDIEndpointRef portRef;
606 std::ostringstream ost;
607 char name[128];
609 std::string stringName;
610 if ( portNumber >= MIDIGetNumberOfDestinations() ) {
611 ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
612 errorString_ = ost.str();
613 error( RtError::WARNING );
614 return stringName;
615 //error( RtError::INVALID_PARAMETER );
618 portRef = MIDIGetDestination( portNumber );
619 nameRef = ConnectedEndpointName(portRef);
620 CFStringGetCString( nameRef, name, sizeof(name), 0);
621 CFRelease( nameRef );
623 return stringName = name;
626 void RtMidiOut :: initialize( const std::string& clientName )
628 // Set up our client.
629 MIDIClientRef client;
630 OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
631 if ( result != noErr ) {
632 errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object.";
633 error( RtError::DRIVER_ERROR );
636 // Save our api-specific connection information.
637 CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
638 data->client = client;
639 data->endpoint = 0;
640 apiData_ = (void *) data;
643 void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
645 if ( connected_ ) {
646 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
647 error( RtError::WARNING );
648 return;
651 unsigned int nDest = MIDIGetNumberOfDestinations();
652 if (nDest < 1) {
653 errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!";
654 error( RtError::NO_DEVICES_FOUND );
657 std::ostringstream ost;
658 if ( portNumber >= nDest ) {
659 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
660 errorString_ = ost.str();
661 error( RtError::INVALID_PARAMETER );
664 MIDIPortRef port;
665 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
666 OSStatus result = MIDIOutputPortCreate( data->client,
667 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
668 &port );
669 if ( result != noErr ) {
670 MIDIClientDispose( data->client );
671 errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port.";
672 error( RtError::DRIVER_ERROR );
675 // Get the desired output port identifier.
676 MIDIEndpointRef destination = MIDIGetDestination( portNumber );
677 if ( destination == 0 ) {
678 MIDIPortDispose( port );
679 MIDIClientDispose( data->client );
680 errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference.";
681 error( RtError::DRIVER_ERROR );
684 // Save our api-specific connection information.
685 data->port = port;
686 data->destinationId = destination;
687 connected_ = true;
690 void RtMidiOut :: closePort( void )
692 if ( connected_ ) {
693 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
694 MIDIPortDispose( data->port );
695 connected_ = false;
699 void RtMidiOut :: openVirtualPort( std::string portName )
701 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
703 if ( data->endpoint ) {
704 errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!";
705 error( RtError::WARNING );
706 return;
709 // Create a virtual MIDI output source.
710 MIDIEndpointRef endpoint;
711 OSStatus result = MIDISourceCreate( data->client,
712 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
713 &endpoint );
714 if ( result != noErr ) {
715 errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source.";
716 error( RtError::DRIVER_ERROR );
719 // Save our api-specific connection information.
720 data->endpoint = endpoint;
723 RtMidiOut :: ~RtMidiOut()
725 // Close a connection if it exists.
726 closePort();
728 // Cleanup.
729 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
730 MIDIClientDispose( data->client );
731 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
732 delete data;
735 char *sysexBuffer = 0;
737 void sysexCompletionProc( MIDISysexSendRequest * sreq )
739 //std::cout << "Completed SysEx send\n";
740 delete sysexBuffer;
741 sysexBuffer = 0;
744 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
746 // We use the MIDISendSysex() function to asynchronously send sysex
747 // messages. Otherwise, we use a single CoreMidi MIDIPacket.
748 unsigned int nBytes = message->size();
749 if ( nBytes == 0 ) {
750 errorString_ = "RtMidiOut::sendMessage: no data in message argument!";
751 error( RtError::WARNING );
752 return;
755 // unsigned int packetBytes, bytesLeft = nBytes;
756 // unsigned int messageIndex = 0;
757 MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
758 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
759 OSStatus result;
761 if ( message->at(0) == 0xF0 ) {
763 while ( sysexBuffer != 0 ) usleep( 1000 ); // sleep 1 ms
765 sysexBuffer = new char[nBytes];
766 if ( sysexBuffer == NULL ) {
767 errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!";
768 error( RtError::MEMORY_ERROR );
771 // Copy data to buffer.
772 for ( unsigned int i=0; i<nBytes; ++i ) sysexBuffer[i] = message->at(i);
774 data->sysexreq.destination = data->destinationId;
775 data->sysexreq.data = (Byte *)sysexBuffer;
776 data->sysexreq.bytesToSend = nBytes;
777 data->sysexreq.complete = 0;
778 data->sysexreq.completionProc = sysexCompletionProc;
779 data->sysexreq.completionRefCon = &(data->sysexreq);
781 result = MIDISendSysex( &(data->sysexreq) );
782 if ( result != noErr ) {
783 errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
784 error( RtError::WARNING );
786 return;
788 else if ( nBytes > 3 ) {
789 errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?";
790 error( RtError::WARNING );
791 return;
794 MIDIPacketList packetList;
795 MIDIPacket *packet = MIDIPacketListInit( &packetList );
796 packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) );
797 if ( !packet ) {
798 errorString_ = "RtMidiOut::sendMessage: could not allocate packet list";
799 error( RtError::DRIVER_ERROR );
802 // Send to any destinations that may have connected to us.
803 if ( data->endpoint ) {
804 result = MIDIReceived( data->endpoint, &packetList );
805 if ( result != noErr ) {
806 errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
807 error( RtError::WARNING );
811 // And send to an explicit destination port if we're connected.
812 if ( connected_ ) {
813 result = MIDISend( data->port, data->destinationId, &packetList );
814 if ( result != noErr ) {
815 errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
816 error( RtError::WARNING );
822 #endif // __MACOSX_CORE__
825 //*********************************************************************//
826 // API: LINUX ALSA SEQUENCER
827 //*********************************************************************//
829 // API information found at:
830 // - http://www.alsa-project.org/documentation.php#Library
832 #if defined(__LINUX_ALSASEQ__)
834 // The ALSA Sequencer API is based on the use of a callback function for
835 // MIDI input.
837 // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
838 // time stamps and other assorted fixes!!!
840 // If you don't need timestamping for incoming MIDI events, define the
841 // preprocessor definition AVOID_TIMESTAMPING to save resources
842 // associated with the ALSA sequencer queues.
844 #include <pthread.h>
845 #include <sys/time.h>
847 // ALSA header file.
848 #include <alsa/asoundlib.h>
850 // A structure to hold variables related to the ALSA API
851 // implementation.
852 struct AlsaMidiData {
853 snd_seq_t *seq;
854 int vport;
855 snd_seq_port_subscribe_t *subscription;
856 snd_midi_event_t *coder;
857 unsigned int bufferSize;
858 unsigned char *buffer;
859 pthread_t thread;
860 unsigned long long lastTime;
861 int queue_id; // an input queue is needed to get timestamped events
864 #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
866 //*********************************************************************//
867 // API: LINUX ALSA
868 // Class Definitions: RtMidiIn
869 //*********************************************************************//
871 extern "C" void *alsaMidiHandler( void *ptr )
873 RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (ptr);
874 AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);
876 long nBytes;
877 unsigned long long time, lastTime;
878 bool continueSysex = false;
879 bool doDecode = false;
880 RtMidiIn::MidiMessage message;
882 snd_seq_event_t *ev;
883 int result;
884 apiData->bufferSize = 32;
885 result = snd_midi_event_new( 0, &apiData->coder );
886 if ( result < 0 ) {
887 data->doInput = false;
888 std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n";
889 return 0;
891 unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );
892 if ( buffer == NULL ) {
893 data->doInput = false;
894 std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n";
895 return 0;
897 snd_midi_event_init( apiData->coder );
898 snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages
900 while ( data->doInput ) {
902 if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {
903 // No data pending ... sleep a bit.
904 usleep( 1000 );
905 continue;
908 // If here, there should be data.
909 result = snd_seq_event_input( apiData->seq, &ev );
910 if ( result == -ENOSPC ) {
911 std::cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n";
912 continue;
914 else if ( result <= 0 ) {
915 std::cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n";
916 continue;
919 // This is a bit weird, but we now have to decode an ALSA MIDI
920 // event (back) into MIDI bytes. We'll ignore non-MIDI types.
921 if ( !continueSysex ) message.bytes.clear();
923 doDecode = false;
924 switch ( ev->type ) {
926 case SND_SEQ_EVENT_PORT_SUBSCRIBED:
927 #if defined(__RTMIDI_DEBUG__)
928 std::cout << "RtMidiIn::alsaMidiHandler: port connection made!\n";
929 #endif
930 break;
932 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
933 #if defined(__RTMIDI_DEBUG__)
934 std::cerr << "RtMidiIn::alsaMidiHandler: port connection has closed!\n";
935 std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"
936 << (int) ev->data.connect.sender.port
937 << ", dest = " << (int) ev->data.connect.dest.client << ":"
938 << (int) ev->data.connect.dest.port
939 << std::endl;
940 #endif
941 break;
943 case SND_SEQ_EVENT_QFRAME: // MIDI time code
944 if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
945 break;
947 case SND_SEQ_EVENT_TICK: // MIDI timing tick
948 if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
949 break;
951 case SND_SEQ_EVENT_SENSING: // Active sensing
952 if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;
953 break;
955 case SND_SEQ_EVENT_SYSEX:
956 if ( (data->ignoreFlags & 0x01) ) break;
957 if ( ev->data.ext.len > apiData->bufferSize ) {
958 apiData->bufferSize = ev->data.ext.len;
959 free( buffer );
960 buffer = (unsigned char *) malloc( apiData->bufferSize );
961 if ( buffer == NULL ) {
962 data->doInput = false;
963 std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n";
964 break;
968 default:
969 doDecode = true;
972 if ( doDecode ) {
974 nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
975 if ( nBytes > 0 ) {
976 // The ALSA sequencer has a maximum buffer size for MIDI sysex
977 // events of 256 bytes. If a device sends sysex messages larger
978 // than this, they are segmented into 256 byte chunks. So,
979 // we'll watch for this and concatenate sysex chunks into a
980 // single sysex message if necessary.
981 if ( !continueSysex )
982 message.bytes.assign( buffer, &buffer[nBytes] );
983 else
984 message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
986 continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
987 if ( !continueSysex ) {
989 // Calculate the time stamp:
990 message.timeStamp = 0.0;
992 // Method 1: Use the system time.
993 //(void)gettimeofday(&tv, (struct timezone *)NULL);
994 //time = (tv.tv_sec * 1000000) + tv.tv_usec;
996 // Method 2: Use the ALSA sequencer event time data.
997 // (thanks to Pedro Lopez-Cabanillas!).
998 time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
999 lastTime = time;
1000 time -= apiData->lastTime;
1001 apiData->lastTime = lastTime;
1002 if ( data->firstMessage == true )
1003 data->firstMessage = false;
1004 else
1005 message.timeStamp = time * 0.000001;
1007 else {
1008 #if defined(__RTMIDI_DEBUG__)
1009 std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
1010 #endif
1015 snd_seq_free_event( ev );
1016 if ( message.bytes.size() == 0 ) continue;
1018 if ( data->usingCallback && !continueSysex ) {
1019 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
1020 callback( message.timeStamp, &message.bytes, data->userData );
1022 else {
1023 // As long as we haven't reached our queue size limit, push the message.
1024 if ( data->queueLimit > data->queue.size() )
1025 data->queue.push( message );
1026 else
1027 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
1031 if ( buffer ) free( buffer );
1032 snd_midi_event_free( apiData->coder );
1033 apiData->coder = 0;
1034 return 0;
1037 void RtMidiIn :: initialize( const std::string& clientName )
1039 // Set up the ALSA sequencer client.
1040 snd_seq_t *seq;
1041 int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
1042 if ( result < 0 ) {
1043 errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object.";
1044 error( RtError::DRIVER_ERROR );
1047 // Set client name.
1048 snd_seq_set_client_name( seq, clientName.c_str() );
1050 // Save our api-specific connection information.
1051 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
1052 data->seq = seq;
1053 data->vport = -1;
1054 apiData_ = (void *) data;
1055 inputData_.apiData = (void *) data;
1057 // Create the input queue
1058 #ifndef AVOID_TIMESTAMPING
1059 data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue");
1060 // Set arbitrary tempo (mm=100) and resolution (240)
1061 snd_seq_queue_tempo_t *qtempo;
1062 snd_seq_queue_tempo_alloca(&qtempo);
1063 snd_seq_queue_tempo_set_tempo(qtempo, 600000);
1064 snd_seq_queue_tempo_set_ppq(qtempo, 240);
1065 snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo);
1066 snd_seq_drain_output(data->seq);
1067 #endif
1070 // This function is used to count or get the pinfo structure for a given port number.
1071 unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )
1073 snd_seq_client_info_t *cinfo;
1074 int client;
1075 int count = 0;
1076 snd_seq_client_info_alloca( &cinfo );
1078 snd_seq_client_info_set_client( cinfo, -1 );
1079 while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {
1080 client = snd_seq_client_info_get_client( cinfo );
1081 if ( client == 0 ) continue;
1082 // Reset query info
1083 snd_seq_port_info_set_client( pinfo, client );
1084 snd_seq_port_info_set_port( pinfo, -1 );
1085 while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
1086 unsigned int atyp = snd_seq_port_info_get_type( pinfo );
1087 if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue;
1088 unsigned int caps = snd_seq_port_info_get_capability( pinfo );
1089 if ( ( caps & type ) != type ) continue;
1090 if ( count == portNumber ) return 1;
1091 ++count;
1095 // If a negative portNumber was used, return the port count.
1096 if ( portNumber < 0 ) return count;
1097 return 0;
1100 void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
1102 if ( connected_ ) {
1103 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
1104 error( RtError::WARNING );
1105 return;
1108 unsigned int nSrc = this->getPortCount();
1109 if (nSrc < 1) {
1110 errorString_ = "RtMidiIn::openPort: no MIDI input sources found!";
1111 error( RtError::NO_DEVICES_FOUND );
1114 snd_seq_port_info_t *pinfo;
1115 snd_seq_port_info_alloca( &pinfo );
1116 std::ostringstream ost;
1117 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1118 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {
1119 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
1120 errorString_ = ost.str();
1121 error( RtError::INVALID_PARAMETER );
1125 snd_seq_addr_t sender, receiver;
1126 sender.client = snd_seq_port_info_get_client( pinfo );
1127 sender.port = snd_seq_port_info_get_port( pinfo );
1128 receiver.client = snd_seq_client_id( data->seq );
1129 if ( data->vport < 0 ) {
1130 snd_seq_port_info_set_client( pinfo, 0 );
1131 snd_seq_port_info_set_port( pinfo, 0 );
1132 snd_seq_port_info_set_capability( pinfo,
1133 SND_SEQ_PORT_CAP_WRITE |
1134 SND_SEQ_PORT_CAP_SUBS_WRITE );
1135 snd_seq_port_info_set_type( pinfo,
1136 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
1137 SND_SEQ_PORT_TYPE_APPLICATION );
1138 snd_seq_port_info_set_midi_channels(pinfo, 16);
1139 #ifndef AVOID_TIMESTAMPING
1140 snd_seq_port_info_set_timestamping(pinfo, 1);
1141 snd_seq_port_info_set_timestamp_real(pinfo, 1);
1142 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
1143 #endif
1144 snd_seq_port_info_set_name(pinfo, portName.c_str() );
1145 data->vport = snd_seq_create_port(data->seq, pinfo);
1147 if ( data->vport < 0 ) {
1148 errorString_ = "RtMidiIn::openPort: ALSA error creating input port.";
1149 error( RtError::DRIVER_ERROR );
1153 receiver.port = data->vport;
1155 // Make subscription
1156 snd_seq_port_subscribe_malloc( &data->subscription );
1157 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
1158 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
1159 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
1160 errorString_ = "RtMidiIn::openPort: ALSA error making port connection.";
1161 error( RtError::DRIVER_ERROR );
1164 if ( inputData_.doInput == false ) {
1165 // Start the input queue
1166 #ifndef AVOID_TIMESTAMPING
1167 snd_seq_start_queue( data->seq, data->queue_id, NULL );
1168 snd_seq_drain_output( data->seq );
1169 #endif
1170 // Start our MIDI input thread.
1171 pthread_attr_t attr;
1172 pthread_attr_init(&attr);
1173 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1174 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
1176 inputData_.doInput = true;
1177 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
1178 pthread_attr_destroy(&attr);
1179 if (err) {
1180 snd_seq_unsubscribe_port( data->seq, data->subscription );
1181 snd_seq_port_subscribe_free( data->subscription );
1182 inputData_.doInput = false;
1183 errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!";
1184 error( RtError::THREAD_ERROR );
1188 connected_ = true;
1191 void RtMidiIn :: openVirtualPort( std::string portName )
1193 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1194 if ( data->vport < 0 ) {
1195 snd_seq_port_info_t *pinfo;
1196 snd_seq_port_info_alloca( &pinfo );
1197 snd_seq_port_info_set_capability( pinfo,
1198 SND_SEQ_PORT_CAP_WRITE |
1199 SND_SEQ_PORT_CAP_SUBS_WRITE );
1200 snd_seq_port_info_set_type( pinfo,
1201 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
1202 SND_SEQ_PORT_TYPE_APPLICATION );
1203 snd_seq_port_info_set_midi_channels(pinfo, 16);
1204 #ifndef AVOID_TIMESTAMPING
1205 snd_seq_port_info_set_timestamping(pinfo, 1);
1206 snd_seq_port_info_set_timestamp_real(pinfo, 1);
1207 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
1208 #endif
1209 snd_seq_port_info_set_name(pinfo, portName.c_str());
1210 data->vport = snd_seq_create_port(data->seq, pinfo);
1212 if ( data->vport < 0 ) {
1213 errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port.";
1214 error( RtError::DRIVER_ERROR );
1218 if ( inputData_.doInput == false ) {
1219 // Start the input queue
1220 #ifndef AVOID_TIMESTAMPING
1221 snd_seq_start_queue( data->seq, data->queue_id, NULL );
1222 snd_seq_drain_output( data->seq );
1223 #endif
1224 // Start our MIDI input thread.
1225 pthread_attr_t attr;
1226 pthread_attr_init(&attr);
1227 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1228 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
1230 inputData_.doInput = true;
1231 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
1232 pthread_attr_destroy(&attr);
1233 if (err) {
1234 snd_seq_unsubscribe_port( data->seq, data->subscription );
1235 snd_seq_port_subscribe_free( data->subscription );
1236 inputData_.doInput = false;
1237 errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!";
1238 error( RtError::THREAD_ERROR );
1243 void RtMidiIn :: closePort( void )
1245 if ( connected_ ) {
1246 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1247 snd_seq_unsubscribe_port( data->seq, data->subscription );
1248 snd_seq_port_subscribe_free( data->subscription );
1249 // Stop the input queue
1250 #ifndef AVOID_TIMESTAMPING
1251 snd_seq_stop_queue( data->seq, data->queue_id, NULL );
1252 snd_seq_drain_output( data->seq );
1253 #endif
1254 connected_ = false;
1258 RtMidiIn :: ~RtMidiIn()
1260 // Close a connection if it exists.
1261 closePort();
1263 // Shutdown the input thread.
1264 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1265 if ( inputData_.doInput ) {
1266 inputData_.doInput = false;
1267 pthread_join( data->thread, NULL );
1270 // Cleanup.
1271 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
1272 #ifndef AVOID_TIMESTAMPING
1273 snd_seq_free_queue( data->seq, data->queue_id );
1274 #endif
1275 snd_seq_close( data->seq );
1276 delete data;
1279 unsigned int RtMidiIn :: getPortCount()
1281 snd_seq_port_info_t *pinfo;
1282 snd_seq_port_info_alloca( &pinfo );
1284 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1285 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );
1288 std::string RtMidiIn :: getPortName( unsigned int portNumber )
1290 snd_seq_client_info_t *cinfo;
1291 snd_seq_port_info_t *pinfo;
1292 snd_seq_client_info_alloca( &cinfo );
1293 snd_seq_port_info_alloca( &pinfo );
1295 std::string stringName;
1296 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1297 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
1298 int cnum = snd_seq_port_info_get_client( pinfo );
1299 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
1300 std::ostringstream os;
1301 os << snd_seq_client_info_get_name( cinfo );
1302 os << ":";
1303 os << snd_seq_port_info_get_port( pinfo );
1304 stringName = os.str();
1305 return stringName;
1308 // If we get here, we didn't find a match.
1309 errorString_ = "RtMidiIn::getPortName: error looking for port name!";
1310 error( RtError::WARNING );
1311 return stringName;
1312 //error( RtError::INVALID_PARAMETER );
1315 //*********************************************************************//
1316 // API: LINUX ALSA
1317 // Class Definitions: RtMidiOut
1318 //*********************************************************************//
1320 unsigned int RtMidiOut :: getPortCount()
1322 snd_seq_port_info_t *pinfo;
1323 snd_seq_port_info_alloca( &pinfo );
1325 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1326 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );
1329 std::string RtMidiOut :: getPortName( unsigned int portNumber )
1331 snd_seq_client_info_t *cinfo;
1332 snd_seq_port_info_t *pinfo;
1333 snd_seq_client_info_alloca( &cinfo );
1334 snd_seq_port_info_alloca( &pinfo );
1336 std::string stringName;
1337 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1338 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
1339 int cnum = snd_seq_port_info_get_client(pinfo);
1340 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
1341 std::ostringstream os;
1342 os << snd_seq_client_info_get_name(cinfo);
1343 os << ":";
1344 os << snd_seq_port_info_get_port(pinfo);
1345 stringName = os.str();
1346 return stringName;
1349 // If we get here, we didn't find a match.
1350 errorString_ = "RtMidiOut::getPortName: error looking for port name!";
1351 //error( RtError::INVALID_PARAMETER );
1352 error( RtError::WARNING );
1353 return stringName;
1356 void RtMidiOut :: initialize( const std::string& clientName )
1358 // Set up the ALSA sequencer client.
1359 snd_seq_t *seq;
1360 int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
1361 if ( result < 0 ) {
1362 errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object.";
1363 error( RtError::DRIVER_ERROR );
1366 // Set client name.
1367 snd_seq_set_client_name( seq, clientName.c_str() );
1369 // Save our api-specific connection information.
1370 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
1371 data->seq = seq;
1372 data->vport = -1;
1373 data->bufferSize = 32;
1374 data->coder = 0;
1375 data->buffer = 0;
1376 result = snd_midi_event_new( data->bufferSize, &data->coder );
1377 if ( result < 0 ) {
1378 delete data;
1379 errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n";
1380 error( RtError::DRIVER_ERROR );
1382 data->buffer = (unsigned char *) malloc( data->bufferSize );
1383 if ( data->buffer == NULL ) {
1384 delete data;
1385 errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n";
1386 error( RtError::MEMORY_ERROR );
1388 snd_midi_event_init( data->coder );
1389 apiData_ = (void *) data;
1392 void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
1394 if ( connected_ ) {
1395 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
1396 error( RtError::WARNING );
1397 return;
1400 unsigned int nSrc = this->getPortCount();
1401 if (nSrc < 1) {
1402 errorString_ = "RtMidiOut::openPort: no MIDI output sources found!";
1403 error( RtError::NO_DEVICES_FOUND );
1406 snd_seq_port_info_t *pinfo;
1407 snd_seq_port_info_alloca( &pinfo );
1408 std::ostringstream ost;
1409 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1410 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {
1411 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
1412 errorString_ = ost.str();
1413 error( RtError::INVALID_PARAMETER );
1416 snd_seq_addr_t sender, receiver;
1417 receiver.client = snd_seq_port_info_get_client( pinfo );
1418 receiver.port = snd_seq_port_info_get_port( pinfo );
1419 sender.client = snd_seq_client_id( data->seq );
1421 if ( data->vport < 0 ) {
1422 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
1423 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
1424 SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
1425 if ( data->vport < 0 ) {
1426 errorString_ = "RtMidiOut::openPort: ALSA error creating output port.";
1427 error( RtError::DRIVER_ERROR );
1431 sender.port = data->vport;
1433 // Make subscription
1434 snd_seq_port_subscribe_malloc( &data->subscription );
1435 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
1436 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
1437 snd_seq_port_subscribe_set_time_update(data->subscription, 1);
1438 snd_seq_port_subscribe_set_time_real(data->subscription, 1);
1439 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
1440 errorString_ = "RtMidiOut::openPort: ALSA error making port connection.";
1441 error( RtError::DRIVER_ERROR );
1444 connected_ = true;
1447 void RtMidiOut :: closePort( void )
1449 if ( connected_ ) {
1450 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1451 snd_seq_unsubscribe_port( data->seq, data->subscription );
1452 snd_seq_port_subscribe_free( data->subscription );
1453 connected_ = false;
1457 void RtMidiOut :: openVirtualPort( std::string portName )
1459 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1460 if ( data->vport < 0 ) {
1461 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
1462 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
1463 SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
1465 if ( data->vport < 0 ) {
1466 errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port.";
1467 error( RtError::DRIVER_ERROR );
1472 RtMidiOut :: ~RtMidiOut()
1474 // Close a connection if it exists.
1475 closePort();
1477 // Cleanup.
1478 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1479 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
1480 if ( data->coder ) snd_midi_event_free( data->coder );
1481 if ( data->buffer ) free( data->buffer );
1482 snd_seq_close( data->seq );
1483 delete data;
1486 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
1488 int result;
1489 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1490 unsigned int nBytes = message->size();
1491 if ( nBytes > data->bufferSize ) {
1492 data->bufferSize = nBytes;
1493 result = snd_midi_event_resize_buffer ( data->coder, nBytes);
1494 if ( result != 0 ) {
1495 errorString_ = "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer.";
1496 error( RtError::DRIVER_ERROR );
1498 free (data->buffer);
1499 data->buffer = (unsigned char *) malloc( data->bufferSize );
1500 if ( data->buffer == NULL ) {
1501 errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n";
1502 error( RtError::MEMORY_ERROR );
1506 snd_seq_event_t ev;
1507 snd_seq_ev_clear(&ev);
1508 snd_seq_ev_set_source(&ev, data->vport);
1509 snd_seq_ev_set_subs(&ev);
1510 snd_seq_ev_set_direct(&ev);
1511 for ( unsigned int i=0; i<nBytes; ++i ) data->buffer[i] = message->at(i);
1512 result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
1513 if ( result < (int)nBytes ) {
1514 errorString_ = "RtMidiOut::sendMessage: event parsing error!";
1515 error( RtError::WARNING );
1516 return;
1519 // Send the event.
1520 result = snd_seq_event_output(data->seq, &ev);
1521 if ( result < 0 ) {
1522 errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
1523 error( RtError::WARNING );
1525 snd_seq_drain_output(data->seq);
1528 #endif // __LINUX_ALSA__
1531 //*********************************************************************//
1532 // API: IRIX MD
1533 //*********************************************************************//
1535 // API information gleamed from:
1536 // http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro
1538 // If the Makefile doesn't work, try the following:
1539 // CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd
1540 // CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd
1541 // CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd
1542 // CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd
1544 #if defined(__IRIX_MD__)
1546 #include <pthread.h>
1547 #include <sys/time.h>
1548 #include <unistd.h>
1550 // Irix MIDI header file.
1551 #include <dmedia/midi.h>
1553 // A structure to hold variables related to the IRIX API
1554 // implementation.
1555 struct IrixMidiData {
1556 MDport port;
1557 pthread_t thread;
1560 //*********************************************************************//
1561 // API: IRIX
1562 // Class Definitions: RtMidiIn
1563 //*********************************************************************//
1565 extern "C" void *irixMidiHandler( void *ptr )
1567 RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (ptr);
1568 IrixMidiData *apiData = static_cast<IrixMidiData *> (data->apiData);
1570 bool continueSysex = false;
1571 unsigned char status;
1572 unsigned short size;
1573 MDevent event;
1574 int fd = mdGetFd( apiData->port );
1575 if ( fd < 0 ) {
1576 data->doInput = false;
1577 std::cerr << "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n";
1578 return 0;
1581 fd_set mask, rmask;
1582 FD_ZERO( &mask );
1583 FD_SET( fd, &mask );
1584 struct timeval timeout = {0, 0};
1585 RtMidiIn::MidiMessage message;
1586 int result;
1588 while ( data->doInput ) {
1590 rmask = mask;
1591 timeout.tv_sec = 0;
1592 timeout.tv_usec = 0;
1593 if ( select( fd+1, &rmask, NULL, NULL, &timeout ) <= 0 ) {
1594 // No data pending ... sleep a bit.
1595 usleep( 1000 );
1596 continue;
1599 // If here, there should be data.
1600 result = mdReceive( apiData->port, &event, 1);
1601 if ( result <= 0 ) {
1602 std::cerr << "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n";
1603 continue;
1606 message.timeStamp = event.stamp * 0.000000001;
1608 size = 0;
1609 status = event.msg[0];
1610 if ( !(status & 0x80) ) continue;
1611 if ( status == 0xF0 ) {
1612 // Sysex message ... can be segmented across multiple messages.
1613 if ( !(data->ignoreFlags & 0x01) ) {
1614 if ( continueSysex ) {
1615 // We have a continuing, segmented sysex message. Append
1616 // the new bytes to our existing message.
1617 for ( int i=0; i<event.msglen; ++i )
1618 message.bytes.push_back( event.sysexmsg[i] );
1619 if ( event.sysexmsg[event.msglen-1] == 0xF7 ) continueSysex = false;
1620 if ( !continueSysex ) {
1621 // If not a continuing sysex message, invoke the user callback function or queue the message.
1622 if ( data->usingCallback && message.bytes.size() > 0 ) {
1623 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
1624 callback( message.timeStamp, &message.bytes, data->userData );
1626 else {
1627 // As long as we haven't reached our queue size limit, push the message.
1628 if ( data->queueLimit > data->queue.size() )
1629 data->queue.push( message );
1630 else
1631 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
1633 message.bytes.clear();
1637 mdFree( NULL );
1638 continue;
1640 else if ( status < 0xC0 ) size = 3;
1641 else if ( status < 0xE0 ) size = 2;
1642 else if ( status < 0xF0 ) size = 3;
1643 else if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) {
1644 // A MIDI time code message and we're not ignoring it.
1645 size = 2;
1647 else if ( status == 0xF2 ) size = 3;
1648 else if ( status == 0xF3 ) size = 2;
1649 else if ( status == 0xF8 ) {
1650 if ( !(data->ignoreFlags & 0x02) ) {
1651 // A MIDI timing tick message and we're not ignoring it.
1652 size = 1;
1655 else if ( status == 0xFE ) { // MIDI active sensing
1656 if ( !(data->ignoreFlags & 0x04) )
1657 size = 1;
1659 else size = 1;
1661 // Copy the MIDI data to our vector.
1662 if ( size ) {
1663 message.bytes.assign( &event.msg[0], &event.msg[size] );
1664 // Invoke the user callback function or queue the message.
1665 if ( data->usingCallback ) {
1666 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
1667 callback( message.timeStamp, &message.bytes, data->userData );
1669 else {
1670 // As long as we haven't reached our queue size limit, push the message.
1671 if ( data->queueLimit > data->queue.size() )
1672 data->queue.push( message );
1673 else
1674 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
1676 message.bytes.clear();
1680 return 0;
1683 void RtMidiIn :: initialize( const std::string& /*clientName*/ )
1685 // Initialize the Irix MIDI system. At the moment, we will not
1686 // worry about a return value of zero (ports) because there is a
1687 // chance the user could plug something in after instantiation.
1688 int nPorts = mdInit();
1690 // Create our api-specific connection information.
1691 IrixMidiData *data = (IrixMidiData *) new IrixMidiData;
1692 apiData_ = (void *) data;
1693 inputData_.apiData = (void *) data;
1696 void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
1698 if ( connected_ ) {
1699 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
1700 error( RtError::WARNING );
1701 return;
1704 int nPorts = mdInit();
1705 if (nPorts < 1) {
1706 errorString_ = "RtMidiIn::openPort: no Irix MIDI input sources found!";
1707 error( RtError::NO_DEVICES_FOUND );
1710 std::ostringstream ost;
1711 if ( portNumber >= nPorts ) {
1712 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
1713 errorString_ = ost.str();
1714 error( RtError::INVALID_PARAMETER );
1717 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
1718 data->port = mdOpenInPort( mdGetName(portNumber) );
1719 if ( data->port == NULL ) {
1720 ost << "RtMidiIn::openPort: Irix error opening the port (" << portNumber << ").";
1721 errorString_ = ost.str();
1722 error( RtError::DRIVER_ERROR );
1724 mdSetStampMode(data->port, MD_DELTASTAMP);
1726 // Start our MIDI input thread.
1727 pthread_attr_t attr;
1728 pthread_attr_init(&attr);
1729 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1730 pthread_attr_setschedpolicy(&attr, SCHED_RR);
1732 inputData_.doInput = true;
1733 int err = pthread_create(&data->thread, &attr, irixMidiHandler, &inputData_);
1734 pthread_attr_destroy(&attr);
1735 if (err) {
1736 mdClosePort( data->port );
1737 inputData_.doInput = false;
1738 errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!";
1739 error( RtError::THREAD_ERROR );
1742 connected_ = true;
1745 void RtMidiIn :: openVirtualPort( std::string portName )
1747 // This function cannot be implemented for the Irix MIDI API.
1748 errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!";
1749 error( RtError::WARNING );
1752 void RtMidiIn :: closePort( void )
1754 if ( connected_ ) {
1755 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
1756 mdClosePort( data->port );
1757 connected_ = false;
1759 // Shutdown the input thread.
1760 inputData_.doInput = false;
1761 pthread_join( data->thread, NULL );
1765 RtMidiIn :: ~RtMidiIn()
1767 // Close a connection if it exists.
1768 closePort();
1770 // Cleanup.
1771 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
1772 delete data;
1775 unsigned int RtMidiIn :: getPortCount()
1777 int nPorts = mdInit();
1778 if ( nPorts >= 0 ) return nPorts;
1779 else return 0;
1782 std::string RtMidiIn :: getPortName( unsigned int portNumber )
1784 int nPorts = mdInit();
1786 std::string stringName;
1787 std::ostringstream ost;
1788 if ( portNumber >= nPorts ) {
1789 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
1790 errorString_ = ost.str();
1791 //error( RtError::INVALID_PARAMETER );
1792 error( RtError::WARNING );
1794 else
1795 std::string stringName = std::string( mdGetName( portNumber ) );
1797 return stringName;
1800 //*********************************************************************//
1801 // API: IRIX MD
1802 // Class Definitions: RtMidiOut
1803 //*********************************************************************//
1805 unsigned int RtMidiOut :: getPortCount()
1807 int nPorts = mdInit();
1808 if ( nPorts >= 0 ) return nPorts;
1809 else return 0;
1812 std::string RtMidiOut :: getPortName( unsigned int portNumber )
1814 int nPorts = mdInit();
1816 std::string stringName;
1817 std::ostringstream ost;
1818 if ( portNumber >= nPorts ) {
1819 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
1820 errorString_ = ost.str();
1821 //error( RtError::INVALID_PARAMETER );
1822 error( RtError::WARNING );
1824 else
1825 std::string stringName = std::string( mdGetName( portNumber ) );
1827 return stringName;
1830 void RtMidiOut :: initialize( const std::string& /*clientName*/ )
1832 // Initialize the Irix MIDI system. At the moment, we will not
1833 // worry about a return value of zero (ports) because there is a
1834 // chance the user could plug something in after instantiation.
1835 int nPorts = mdInit();
1837 // Create our api-specific connection information.
1838 IrixMidiData *data = (IrixMidiData *) new IrixMidiData;
1839 apiData_ = (void *) data;
1842 void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
1844 if ( connected_ ) {
1845 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
1846 error( RtError::WARNING );
1847 return;
1850 int nPorts = mdInit();
1851 if (nPorts < 1) {
1852 errorString_ = "RtMidiOut::openPort: no Irix MIDI output sources found!";
1853 error( RtError::NO_DEVICES_FOUND );
1856 std::ostringstream ost;
1857 if ( portNumber >= nPorts ) {
1858 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
1859 errorString_ = ost.str();
1860 error( RtError::INVALID_PARAMETER );
1863 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
1864 data->port = mdOpenOutPort( mdGetName(portNumber) );
1865 if ( data->port == NULL ) {
1866 ost << "RtMidiOut::openPort: Irix error opening the port (" << portNumber << ").";
1867 errorString_ = ost.str();
1868 error( RtError::DRIVER_ERROR );
1870 mdSetStampMode(data->port, MD_NOSTAMP);
1872 connected_ = true;
1875 void RtMidiOut :: closePort( void )
1877 if ( connected_ ) {
1878 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
1879 mdClosePort( data->port );
1880 connected_ = false;
1884 void RtMidiOut :: openVirtualPort( std::string portName )
1886 // This function cannot be implemented for the Irix MIDI API.
1887 errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!";
1888 error( RtError::WARNING );
1891 RtMidiOut :: ~RtMidiOut()
1893 // Close a connection if it exists.
1894 closePort();
1896 // Cleanup.
1897 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
1898 delete data;
1901 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
1903 int result;
1904 MDevent event;
1905 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
1906 char *buffer = 0;
1908 unsigned int nBytes = message->size();
1909 if ( nBytes == 0 ) return;
1910 event.stamp = 0;
1911 if ( message->at(0) == 0xF0 ) {
1912 if ( nBytes < 3 ) return; // check for bogus sysex
1913 event.msg[0] = 0xF0;
1914 event.msglen = nBytes;
1915 buffer = (char *) malloc( nBytes );
1916 for ( int i=0; i<nBytes; ++i ) buffer[i] = message->at(i);
1917 event.sysexmsg = buffer;
1919 else {
1920 for ( int i=0; i<nBytes; ++i )
1921 event.msg[i] = message->at(i);
1924 // Send the event.
1925 result = mdSend( data->port, &event, 1 );
1926 if ( buffer ) free( buffer );
1927 if ( result < 1 ) {
1928 errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!";
1929 error( RtError::WARNING );
1930 return;
1934 #endif // __IRIX_MD__
1936 //*********************************************************************//
1937 // API: Windows Multimedia Library (MM)
1938 //*********************************************************************//
1940 // API information deciphered from:
1941 // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
1943 // Thanks to Jean-Baptiste Berruchon for the sysex code.
1945 #if defined(__WINDOWS_MM__)
1947 // The Windows MM API is based on the use of a callback function for
1948 // MIDI input. We convert the system specific time stamps to delta
1949 // time values.
1951 // Windows MM MIDI header files.
1952 #include <windows.h>
1953 #include <mmsystem.h>
1955 #define RT_SYSEX_BUFFER_SIZE 1024
1956 #define RT_SYSEX_BUFFER_COUNT 4
1958 // A structure to hold variables related to the CoreMIDI API
1959 // implementation.
1960 struct WinMidiData {
1961 HMIDIIN inHandle; // Handle to Midi Input Device
1962 HMIDIOUT outHandle; // Handle to Midi Output Device
1963 DWORD lastTime;
1964 RtMidiIn::MidiMessage message;
1965 LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];
1968 //*********************************************************************//
1969 // API: Windows MM
1970 // Class Definitions: RtMidiIn
1971 //*********************************************************************//
1973 static void CALLBACK midiInputCallback( HMIDIIN hmin,
1974 UINT inputStatus,
1975 DWORD_PTR instancePtr,
1976 DWORD_PTR midiMessage,
1977 DWORD timestamp )
1979 if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;
1981 //RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (instancePtr);
1982 RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr;
1983 WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);
1985 // Calculate time stamp.
1986 apiData->message.timeStamp = 0.0;
1987 if ( data->firstMessage == true ) data->firstMessage = false;
1988 else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;
1989 apiData->lastTime = timestamp;
1991 if ( inputStatus == MIM_DATA ) { // Channel or system message
1993 // Make sure the first byte is a status byte.
1994 unsigned char status = (unsigned char) (midiMessage & 0x000000FF);
1995 if ( !(status & 0x80) ) return;
1997 // Determine the number of bytes in the MIDI message.
1998 unsigned short nBytes = 1;
1999 if ( status < 0xC0 ) nBytes = 3;
2000 else if ( status < 0xE0 ) nBytes = 2;
2001 else if ( status < 0xF0 ) nBytes = 3;
2002 else if ( status == 0xF1 ) {
2003 if ( data->ignoreFlags & 0x02 ) return;
2004 else nBytes = 2;
2006 else if ( status == 0xF2 ) nBytes = 3;
2007 else if ( status == 0xF3 ) nBytes = 2;
2008 else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {
2009 // A MIDI timing tick message and we're ignoring it.
2010 return;
2012 else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
2013 // A MIDI active sensing message and we're ignoring it.
2014 return;
2017 // Copy bytes to our MIDI message.
2018 unsigned char *ptr = (unsigned char *) &midiMessage;
2019 for ( int i=0; i<nBytes; ++i ) apiData->message.bytes.push_back( *ptr++ );
2021 else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )
2022 MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
2023 if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) {
2024 // Sysex message and we're not ignoring it
2025 for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i )
2026 apiData->message.bytes.push_back( sysex->lpData[i] );
2029 // The WinMM API requires that the sysex buffer be requeued after
2030 // input of each sysex message. Even if we are ignoring sysex
2031 // messages, we still need to requeue the buffer in case the user
2032 // decides to not ignore sysex messages in the future. However,
2033 // it seems that WinMM calls this function with an empty sysex
2034 // buffer when an application closes and in this case, we should
2035 // avoid requeueing it, else the computer suddenly reboots after
2036 // one or two minutes.
2037 if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {
2038 //if ( sysex->dwBytesRecorded > 0 ) {
2039 MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );
2040 if ( result != MMSYSERR_NOERROR )
2041 std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
2043 if ( data->ignoreFlags & 0x01 ) return;
2045 else return;
2048 if ( data->usingCallback ) {
2049 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
2050 callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );
2052 else {
2053 // As long as we haven't reached our queue size limit, push the message.
2054 if ( data->queueLimit > data->queue.size() )
2055 data->queue.push( apiData->message );
2056 else
2057 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
2060 // Clear the vector for the next input message.
2061 apiData->message.bytes.clear();
2064 void RtMidiIn :: initialize( const std::string& /*clientName*/ )
2066 // We'll issue a warning here if no devices are available but not
2067 // throw an error since the user can plugin something later.
2068 unsigned int nDevices = midiInGetNumDevs();
2069 if ( nDevices == 0 ) {
2070 errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available.";
2071 error( RtError::WARNING );
2074 // Save our api-specific connection information.
2075 WinMidiData *data = (WinMidiData *) new WinMidiData;
2076 apiData_ = (void *) data;
2077 inputData_.apiData = (void *) data;
2078 data->message.bytes.clear(); // needs to be empty for first input message
2081 void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
2083 if ( connected_ ) {
2084 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
2085 error( RtError::WARNING );
2086 return;
2089 unsigned int nDevices = midiInGetNumDevs();
2090 if (nDevices == 0) {
2091 errorString_ = "RtMidiIn::openPort: no MIDI input sources found!";
2092 error( RtError::NO_DEVICES_FOUND );
2095 std::ostringstream ost;
2096 if ( portNumber >= nDevices ) {
2097 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
2098 errorString_ = ost.str();
2099 error( RtError::INVALID_PARAMETER );
2102 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2103 MMRESULT result = midiInOpen( &data->inHandle,
2104 portNumber,
2105 (DWORD_PTR)&midiInputCallback,
2106 (DWORD_PTR)&inputData_,
2107 CALLBACK_FUNCTION );
2108 if ( result != MMSYSERR_NOERROR ) {
2109 errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port.";
2110 error( RtError::DRIVER_ERROR );
2113 // Allocate and init the sysex buffers.
2114 for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
2115 data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
2116 data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
2117 data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
2118 data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
2119 data->sysexBuffer[i]->dwFlags = 0;
2121 result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
2122 if ( result != MMSYSERR_NOERROR ) {
2123 midiInClose( data->inHandle );
2124 errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
2125 error( RtError::DRIVER_ERROR );
2128 // Register the buffer.
2129 result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
2130 if ( result != MMSYSERR_NOERROR ) {
2131 midiInClose( data->inHandle );
2132 errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (AddBuffer).";
2133 error( RtError::DRIVER_ERROR );
2137 result = midiInStart( data->inHandle );
2138 if ( result != MMSYSERR_NOERROR ) {
2139 midiInClose( data->inHandle );
2140 errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port.";
2141 error( RtError::DRIVER_ERROR );
2144 connected_ = true;
2147 void RtMidiIn :: openVirtualPort( std::string portName )
2149 // This function cannot be implemented for the Windows MM MIDI API.
2150 errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
2151 error( RtError::WARNING );
2154 void RtMidiIn :: closePort( void )
2156 if ( connected_ ) {
2157 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2158 midiInReset( data->inHandle );
2159 midiInStop( data->inHandle );
2161 for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
2162 int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
2163 delete [] data->sysexBuffer[i]->lpData;
2164 delete [] data->sysexBuffer[i];
2165 if ( result != MMSYSERR_NOERROR ) {
2166 midiInClose( data->inHandle );
2167 errorString_ = "RtMidiIn::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
2168 error( RtError::DRIVER_ERROR );
2172 midiInClose( data->inHandle );
2173 connected_ = false;
2177 RtMidiIn :: ~RtMidiIn()
2179 // Close a connection if it exists.
2180 closePort();
2182 // Cleanup.
2183 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2184 delete data;
2187 unsigned int RtMidiIn :: getPortCount()
2189 return midiInGetNumDevs();
2192 std::string RtMidiIn :: getPortName( unsigned int portNumber )
2194 std::string stringName;
2195 unsigned int nDevices = midiInGetNumDevs();
2196 if ( portNumber >= nDevices ) {
2197 std::ostringstream ost;
2198 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2199 errorString_ = ost.str();
2200 //error( RtError::INVALID_PARAMETER );
2201 error( RtError::WARNING );
2202 return stringName;
2205 MIDIINCAPS deviceCaps;
2206 midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
2208 // For some reason, we need to copy character by character with
2209 // UNICODE (thanks to Eduardo Coutinho!).
2210 //std::string stringName = std::string( deviceCaps.szPname );
2211 char nameString[MAXPNAMELEN];
2212 for( int i=0; i<MAXPNAMELEN; ++i )
2213 nameString[i] = (char)( deviceCaps.szPname[i] );
2215 stringName = nameString;
2216 return stringName;
2219 //*********************************************************************//
2220 // API: Windows MM
2221 // Class Definitions: RtMidiOut
2222 //*********************************************************************//
2224 unsigned int RtMidiOut :: getPortCount()
2226 return midiOutGetNumDevs();
2229 std::string RtMidiOut :: getPortName( unsigned int portNumber )
2231 std::string stringName;
2232 unsigned int nDevices = midiOutGetNumDevs();
2233 if ( portNumber >= nDevices ) {
2234 std::ostringstream ost;
2235 ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2236 errorString_ = ost.str();
2237 //error( RtError::INVALID_PARAMETER );
2238 error( RtError::WARNING );
2239 return stringName;
2242 MIDIOUTCAPS deviceCaps;
2243 midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));
2245 // For some reason, we need to copy character by character with
2246 // UNICODE (thanks to Eduardo Coutinho!).
2247 //std::string stringName = std::string( deviceCaps.szPname );
2248 char nameString[MAXPNAMELEN];
2249 for( int i=0; i<MAXPNAMELEN; ++i )
2250 nameString[i] = (char)( deviceCaps.szPname[i] );
2252 stringName = nameString;
2253 return stringName;
2256 void RtMidiOut :: initialize( const std::string& /*clientName*/ )
2258 // We'll issue a warning here if no devices are available but not
2259 // throw an error since the user can plug something in later.
2260 unsigned int nDevices = midiOutGetNumDevs();
2261 if ( nDevices == 0 ) {
2262 errorString_ = "RtMidiOut::initialize: no MIDI output devices currently available.";
2263 error( RtError::WARNING );
2266 // Save our api-specific connection information.
2267 WinMidiData *data = (WinMidiData *) new WinMidiData;
2268 apiData_ = (void *) data;
2271 void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
2273 if ( connected_ ) {
2274 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
2275 error( RtError::WARNING );
2276 return;
2279 unsigned int nDevices = midiOutGetNumDevs();
2280 if (nDevices < 1) {
2281 errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!";
2282 error( RtError::NO_DEVICES_FOUND );
2285 std::ostringstream ost;
2286 if ( portNumber >= nDevices ) {
2287 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
2288 errorString_ = ost.str();
2289 error( RtError::INVALID_PARAMETER );
2292 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2293 MMRESULT result = midiOutOpen( &data->outHandle,
2294 portNumber,
2295 (DWORD)NULL,
2296 (DWORD)NULL,
2297 CALLBACK_NULL );
2298 if ( result != MMSYSERR_NOERROR ) {
2299 errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port.";
2300 error( RtError::DRIVER_ERROR );
2303 connected_ = true;
2306 void RtMidiOut :: closePort( void )
2308 if ( connected_ ) {
2309 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2310 midiOutReset( data->outHandle );
2311 midiOutClose( data->outHandle );
2312 connected_ = false;
2316 void RtMidiOut :: openVirtualPort( std::string portName )
2318 // This function cannot be implemented for the Windows MM MIDI API.
2319 errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
2320 error( RtError::WARNING );
2323 RtMidiOut :: ~RtMidiOut()
2325 // Close a connection if it exists.
2326 closePort();
2328 // Cleanup.
2329 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2330 delete data;
2333 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
2335 unsigned int nBytes = static_cast<unsigned int>(message->size());
2336 if ( nBytes == 0 ) {
2337 errorString_ = "RtMidiOut::sendMessage: message argument is empty!";
2338 error( RtError::WARNING );
2339 return;
2342 MMRESULT result;
2343 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2344 if ( message->at(0) == 0xF0 ) { // Sysex message
2346 // Allocate buffer for sysex data.
2347 char *buffer = (char *) malloc( nBytes );
2348 if ( buffer == NULL ) {
2349 errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!";
2350 error( RtError::MEMORY_ERROR );
2353 // Copy data to buffer.
2354 for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message->at(i);
2356 // Create and prepare MIDIHDR structure.
2357 MIDIHDR sysex;
2358 sysex.lpData = (LPSTR) buffer;
2359 sysex.dwBufferLength = nBytes;
2360 sysex.dwFlags = 0;
2361 result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) );
2362 if ( result != MMSYSERR_NOERROR ) {
2363 free( buffer );
2364 errorString_ = "RtMidiOut::sendMessage: error preparing sysex header.";
2365 error( RtError::DRIVER_ERROR );
2368 // Send the message.
2369 result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) );
2370 if ( result != MMSYSERR_NOERROR ) {
2371 free( buffer );
2372 errorString_ = "RtMidiOut::sendMessage: error sending sysex message.";
2373 error( RtError::DRIVER_ERROR );
2376 // Unprepare the buffer and MIDIHDR.
2377 while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 );
2378 free( buffer );
2381 else { // Channel or system message.
2383 // Make sure the message size isn't too big.
2384 if ( nBytes > 3 ) {
2385 errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!";
2386 error( RtError::WARNING );
2387 return;
2390 // Pack MIDI bytes into double word.
2391 DWORD packet;
2392 unsigned char *ptr = (unsigned char *) &packet;
2393 for ( unsigned int i=0; i<nBytes; ++i ) {
2394 *ptr = message->at(i);
2395 ++ptr;
2398 // Send the message immediately.
2399 result = midiOutShortMsg( data->outHandle, packet );
2400 if ( result != MMSYSERR_NOERROR ) {
2401 errorString_ = "RtMidiOut::sendMessage: error sending MIDI message.";
2402 error( RtError::DRIVER_ERROR );
2407 #endif // __WINDOWS_MM__
2409 //*********************************************************************//
2410 // API: LINUX JACK
2412 // Written primarily by Alexander Svetalkin, with updates for delta
2413 // time by Gary Scavone, April 2011.
2415 // *********************************************************************//
2417 #if defined(__LINUX_JACK__)
2419 // JACK header files
2420 #include <jack/jack.h>
2421 #include <jack/midiport.h>
2422 #include <jack/ringbuffer.h>
2424 #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
2426 struct JackMidiData {
2427 jack_client_t *client;
2428 jack_port_t *port;
2429 jack_ringbuffer_t *buffSize;
2430 jack_ringbuffer_t *buffMessage;
2431 jack_time_t lastTime;
2434 struct Arguments {
2435 JackMidiData *jackData;
2436 RtMidiIn :: RtMidiInData *rtMidiIn;
2439 //*********************************************************************//
2440 // API: JACK
2441 // Class Definitions: RtMidiIn
2442 //*********************************************************************//
2444 int jackProcessIn( jack_nframes_t nframes, void *arg )
2446 JackMidiData *jData = ( (Arguments *) arg )->jackData;
2447 RtMidiIn :: RtMidiInData *rtData = ( (Arguments *) arg )->rtMidiIn;
2448 jack_midi_event_t event;
2449 jack_time_t long long time;
2451 // Is port created?
2452 if ( jData->port == NULL ) return 0;
2453 void *buff = jack_port_get_buffer( jData->port, nframes );
2455 // We have midi events in buffer
2456 int evCount = jack_midi_get_event_count( buff );
2457 if ( evCount > 0 ) {
2458 RtMidiIn::MidiMessage message;
2459 message.bytes.clear();
2461 jack_midi_event_get( &event, buff, 0 );
2463 for (unsigned int i = 0; i < event.size; i++ )
2464 message.bytes.push_back( event.buffer[i] );
2466 // Compute the delta time.
2467 time = jack_get_time();
2468 if ( rtData->firstMessage == true )
2469 rtData->firstMessage = false;
2470 else
2471 message.timeStamp = ( time - jData->lastTime ) * 0.000001;
2473 jData->lastTime = time;
2475 if ( rtData->usingCallback && !rtData->continueSysex ) {
2476 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;
2477 callback( message.timeStamp, &message.bytes, rtData->userData );
2479 else {
2480 // As long as we haven't reached our queue size limit, push the message.
2481 if ( rtData->queueLimit > rtData->queue.size() )
2482 rtData->queue.push( message );
2483 else
2484 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
2488 return 0;
2491 void RtMidiIn :: initialize( const std::string& clientName )
2493 JackMidiData *data = new JackMidiData;
2495 // Initialize JACK client
2496 if (( data->client = jack_client_open( clientName.c_str(), JackNullOption, NULL )) == 0)
2498 errorString_ = "RtMidiOut::initialize: JACK server not running?";
2499 error( RtError::DRIVER_ERROR );
2500 return;
2503 Arguments *arg = new Arguments;
2504 arg->jackData = data;
2506 arg->rtMidiIn = &inputData_;
2507 jack_set_process_callback( data->client, jackProcessIn, arg );
2508 data->port = NULL;
2509 jack_activate( data->client );
2511 apiData_ = (void *) data;
2514 RtMidiIn :: ~RtMidiIn()
2516 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2517 jack_client_close( data->client );
2520 void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
2522 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2524 // Creating new port
2525 if ( data->port == NULL)
2526 data->port = jack_port_register( data->client, portName.c_str(),
2527 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
2529 if ( data->port == NULL) {
2530 errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
2531 error( RtError::DRIVER_ERROR );
2534 // Connecting to the output
2535 std::string name = getPortName( portNumber );
2536 jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );
2539 void RtMidiIn :: openVirtualPort( const std::string portName )
2541 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2543 if ( data->port == NULL )
2544 data->port = jack_port_register( data->client, portName.c_str(),
2545 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
2547 if ( data->port == NULL ) {
2548 errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
2549 error( RtError::DRIVER_ERROR );
2553 unsigned int RtMidiIn :: getPortCount()
2555 int count = 0;
2556 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2558 // List of available ports
2559 const char **ports = jack_get_ports( data->client, NULL,
2560 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
2562 if ( ports == NULL ) return 0;
2563 while ( ports[count] != NULL )
2564 count++;
2566 free( ports );
2568 return count;
2571 std::string RtMidiIn :: getPortName( unsigned int portNumber )
2573 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2574 std::ostringstream ost;
2575 std::string retStr("");
2577 // List of available ports
2578 const char **ports = jack_get_ports( data->client, NULL,
2579 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
2581 // Check port validity
2582 if ( ports == NULL ) {
2583 errorString_ = "RtMidiOut::getPortName: no ports available!";
2584 error( RtError::WARNING );
2585 return retStr;
2588 if ( ports[portNumber] == NULL ) {
2589 ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2590 errorString_ = ost.str();
2591 error( RtError::WARNING );
2593 else retStr.assign( ports[portNumber] );
2595 free( ports );
2597 return retStr;
2600 void RtMidiIn :: closePort()
2602 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2604 if ( data->port == NULL ) return;
2605 jack_port_unregister( data->client, data->port );
2608 //*********************************************************************//
2609 // API: JACK
2610 // Class Definitions: RtMidiOut
2611 //*********************************************************************//
2613 // Jack process callback
2614 int jackProcessOut( jack_nframes_t nframes, void *arg )
2616 JackMidiData *data = (JackMidiData *) arg;
2617 jack_midi_data_t *midiData;
2618 int space;
2620 // Is port created?
2621 if ( data->port == NULL ) return 0;
2623 void *buff = jack_port_get_buffer( data->port, nframes );
2624 jack_midi_clear_buffer( buff );
2626 while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
2627 jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) );
2628 midiData = jack_midi_event_reserve( buff, 0, space );
2630 jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );
2633 return 0;
2636 void RtMidiOut :: initialize( const std::string& clientName )
2638 JackMidiData *data = new JackMidiData;
2640 // Initialize JACK client
2641 if (( data->client = jack_client_open( clientName.c_str(), JackNullOption, NULL )) == 0)
2643 errorString_ = "RtMidiOut::initialize: JACK server not running?";
2644 error( RtError::DRIVER_ERROR );
2645 return;
2648 jack_set_process_callback( data->client, jackProcessOut, data );
2649 data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
2650 data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
2651 jack_activate( data->client );
2653 data->port = NULL;
2655 apiData_ = (void *) data;
2658 RtMidiOut :: ~RtMidiOut()
2660 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2662 // Cleanup
2663 jack_client_close( data->client );
2664 jack_ringbuffer_free( data->buffSize );
2665 jack_ringbuffer_free( data->buffMessage );
2668 void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
2670 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2672 // Creating new port
2673 if ( data->port == NULL )
2674 data->port = jack_port_register( data->client, portName.c_str(),
2675 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
2677 if ( data->port == NULL ) {
2678 errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
2679 error( RtError::DRIVER_ERROR );
2682 // Connecting to the output
2683 std::string name = getPortName( portNumber );
2684 jack_connect( data->client, jack_port_name( data->port ), name.c_str() );
2687 void RtMidiOut :: openVirtualPort( const std::string portName )
2689 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2691 if ( data->port == NULL )
2692 data->port = jack_port_register( data->client, portName.c_str(),
2693 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
2695 if ( data->port == NULL ) {
2696 errorString_ = "RtMidiOut::openVirtualPort: JACK error creating virtual port";
2697 error( RtError::DRIVER_ERROR );
2701 unsigned int RtMidiOut :: getPortCount()
2703 int count = 0;
2704 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2706 // List of available ports
2707 const char **ports = jack_get_ports( data->client, NULL,
2708 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
2710 if ( ports == NULL ) return 0;
2711 while ( ports[count] != NULL )
2712 count++;
2714 free( ports );
2716 return count;
2719 std::string RtMidiOut :: getPortName( unsigned int portNumber )
2721 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2722 std::ostringstream ost;
2723 std::string retStr("");
2725 // List of available ports
2726 const char **ports = jack_get_ports( data->client, NULL,
2727 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
2729 // Check port validity
2730 if ( ports == NULL) {
2731 errorString_ = "RtMidiOut::getPortName: no ports available!";
2732 error( RtError::WARNING );
2733 return retStr;
2736 if ( ports[portNumber] == NULL) {
2737 ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2738 errorString_ = ost.str();
2739 error( RtError::WARNING );
2741 else retStr.assign( ports[portNumber] );
2743 free( ports );
2745 return retStr;
2748 void RtMidiOut :: closePort()
2750 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2752 if ( data->port == NULL ) return;
2753 jack_port_unregister( data->client, data->port );
2754 data->port = NULL;
2757 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
2759 int nBytes = message->size();
2760 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2762 // Write full message to buffer
2763 jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0],
2764 message->size() );
2765 jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
2768 #endif // __LINUX_JACK__