1 /**********************************************************************/
2 /*! \class RtMidi
3     \brief An abstract base class for realtime MIDI input/output.
4 
5     This class implements some common functionality for the realtime
6     MIDI input/output subclasses RtMidiIn and RtMidiOut.
7 
8     RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
9 
10     RtMidi: realtime MIDI i/o C++ classes
11     Copyright (c) 2003-2017 Gary P. Scavone
12 
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:
20 
21     The above copyright notice and this permission notice shall be
22     included in all copies or substantial portions of the Software.
23 
24     Any person wishing to distribute modifications to the Software is
25     asked to send the modifications to the original developer so that
26     they can be incorporated into the canonical version.  This is,
27     however, not a binding provision of this license.
28 
29     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
32     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
33     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
34     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37 /**********************************************************************/
38 
39 #include "RtMidi.h"
40 #include <sstream>
41 
42 #if defined(__MACOSX_CORE__)
43   #if TARGET_OS_IPHONE
44     #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
45     #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
46   #endif
47 #endif
48 
49 // Default for Windows is to add an identifier to the port names; this
50 // flag can be defined (e.g. in your project file) to disable this behaviour.
51 //#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
52 
53 //*********************************************************************//
54 //  RtMidi Definitions
55 //*********************************************************************//
56 
RtMidi()57 RtMidi :: RtMidi()
58   : rtapi_(0)
59 {
60 }
61 
~RtMidi()62 RtMidi :: ~RtMidi()
63 {
64   delete rtapi_;
65   rtapi_ = 0;
66 }
67 
getVersion(void)68 std::string RtMidi :: getVersion( void ) throw()
69 {
70   return std::string( RTMIDI_VERSION );
71 }
72 
getCompiledApi(std::vector<RtMidi::Api> & apis)73 void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()
74 {
75   apis.clear();
76 
77   // The order here will control the order of RtMidi's API search in
78   // the constructor.
79 #if defined(__MACOSX_CORE__)
80   apis.push_back( MACOSX_CORE );
81 #endif
82 #if defined(__LINUX_ALSA__)
83   apis.push_back( LINUX_ALSA );
84 #endif
85 #if defined(__UNIX_JACK__)
86   apis.push_back( UNIX_JACK );
87 #endif
88 #if defined(__WINDOWS_MM__)
89   apis.push_back( WINDOWS_MM );
90 #endif
91 #if defined(__RTMIDI_DUMMY__)
92   apis.push_back( RTMIDI_DUMMY );
93 #endif
94 }
95 
96 //*********************************************************************//
97 //  RtMidiIn Definitions
98 //*********************************************************************//
99 
openMidiApi(RtMidi::Api api,const std::string & clientName,unsigned int queueSizeLimit)100 void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit )
101 {
102   delete rtapi_;
103   rtapi_ = 0;
104 
105 #if defined(__UNIX_JACK__)
106   if ( api == UNIX_JACK )
107     rtapi_ = new MidiInJack( clientName, queueSizeLimit );
108 #endif
109 #if defined(__LINUX_ALSA__)
110   if ( api == LINUX_ALSA )
111     rtapi_ = new MidiInAlsa( clientName, queueSizeLimit );
112 #endif
113 #if defined(__WINDOWS_MM__)
114   if ( api == WINDOWS_MM )
115     rtapi_ = new MidiInWinMM( clientName, queueSizeLimit );
116 #endif
117 #if defined(__MACOSX_CORE__)
118   if ( api == MACOSX_CORE )
119     rtapi_ = new MidiInCore( clientName, queueSizeLimit );
120 #endif
121 #if defined(__RTMIDI_DUMMY__)
122   if ( api == RTMIDI_DUMMY )
123     rtapi_ = new MidiInDummy( clientName, queueSizeLimit );
124 #endif
125 }
126 
RtMidiIn(RtMidi::Api api,const std::string & clientName,unsigned int queueSizeLimit)127 RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit )
128   : RtMidi()
129 {
130   if ( api != UNSPECIFIED ) {
131     // Attempt to open the specified API.
132     openMidiApi( api, clientName, queueSizeLimit );
133     if ( rtapi_ ) return;
134 
135     // No compiled support for specified API value.  Issue a warning
136     // and continue as if no API was specified.
137     std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl;
138   }
139 
140   // Iterate through the compiled APIs and return as soon as we find
141   // one with at least one port or we reach the end of the list.
142   std::vector< RtMidi::Api > apis;
143   getCompiledApi( apis );
144   for ( unsigned int i=0; i<apis.size(); i++ ) {
145     openMidiApi( apis[i], clientName, queueSizeLimit );
146     if ( rtapi_ && rtapi_->getPortCount() ) break;
147   }
148 
149   if ( rtapi_ ) return;
150 
151   // It should not be possible to get here because the preprocessor
152   // definition __RTMIDI_DUMMY__ is automatically defined if no
153   // API-specific definitions are passed to the compiler. But just in
154   // case something weird happens, we'll throw an error.
155   std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!";
156   throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );
157 }
158 
~RtMidiIn()159 RtMidiIn :: ~RtMidiIn() throw()
160 {
161 }
162 
163 
164 //*********************************************************************//
165 //  RtMidiOut Definitions
166 //*********************************************************************//
167 
openMidiApi(RtMidi::Api api,const std::string & clientName)168 void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName )
169 {
170   delete rtapi_;
171   rtapi_ = 0;
172 
173 #if defined(__UNIX_JACK__)
174   if ( api == UNIX_JACK )
175     rtapi_ = new MidiOutJack( clientName );
176 #endif
177 #if defined(__LINUX_ALSA__)
178   if ( api == LINUX_ALSA )
179     rtapi_ = new MidiOutAlsa( clientName );
180 #endif
181 #if defined(__WINDOWS_MM__)
182   if ( api == WINDOWS_MM )
183     rtapi_ = new MidiOutWinMM( clientName );
184 #endif
185 #if defined(__MACOSX_CORE__)
186   if ( api == MACOSX_CORE )
187     rtapi_ = new MidiOutCore( clientName );
188 #endif
189 #if defined(__RTMIDI_DUMMY__)
190   if ( api == RTMIDI_DUMMY )
191     rtapi_ = new MidiOutDummy( clientName );
192 #endif
193 }
194 
RtMidiOut(RtMidi::Api api,const std::string & clientName)195 RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName)
196 {
197   if ( api != UNSPECIFIED ) {
198     // Attempt to open the specified API.
199     openMidiApi( api, clientName );
200     if ( rtapi_ ) return;
201 
202     // No compiled support for specified API value.  Issue a warning
203     // and continue as if no API was specified.
204     std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl;
205   }
206 
207   // Iterate through the compiled APIs and return as soon as we find
208   // one with at least one port or we reach the end of the list.
209   std::vector< RtMidi::Api > apis;
210   getCompiledApi( apis );
211   for ( unsigned int i=0; i<apis.size(); i++ ) {
212     openMidiApi( apis[i], clientName );
213     if ( rtapi_ && rtapi_->getPortCount() ) break;
214   }
215 
216   if ( rtapi_ ) return;
217 
218   // It should not be possible to get here because the preprocessor
219   // definition __RTMIDI_DUMMY__ is automatically defined if no
220   // API-specific definitions are passed to the compiler. But just in
221   // case something weird happens, we'll thrown an error.
222   std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!";
223   throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );
224 }
225 
~RtMidiOut()226 RtMidiOut :: ~RtMidiOut() throw()
227 {
228 }
229 
230 //*********************************************************************//
231 //  Common MidiApi Definitions
232 //*********************************************************************//
233 
MidiApi(void)234 MidiApi :: MidiApi( void )
235   : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0)
236 {
237 }
238 
~MidiApi(void)239 MidiApi :: ~MidiApi( void )
240 {
241 }
242 
setErrorCallback(RtMidiErrorCallback errorCallback,void * userData=0)243 void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 )
244 {
245     errorCallback_ = errorCallback;
246     errorCallbackUserData_ = userData;
247 }
248 
error(RtMidiError::Type type,std::string errorString)249 void MidiApi :: error( RtMidiError::Type type, std::string errorString )
250 {
251   if ( errorCallback_ ) {
252 
253     if ( firstErrorOccurred_ )
254       return;
255 
256     firstErrorOccurred_ = true;
257     const std::string errorMessage = errorString;
258 
259     errorCallback_( type, errorMessage, errorCallbackUserData_);
260     firstErrorOccurred_ = false;
261     return;
262   }
263 
264   if ( type == RtMidiError::WARNING ) {
265     std::cerr << '\n' << errorString << "\n\n";
266   }
267   else if ( type == RtMidiError::DEBUG_WARNING ) {
268 #if defined(__RTMIDI_DEBUG__)
269     std::cerr << '\n' << errorString << "\n\n";
270 #endif
271   }
272   else {
273     std::cerr << '\n' << errorString << "\n\n";
274     throw RtMidiError( errorString, type );
275   }
276 }
277 
278 //*********************************************************************//
279 //  Common MidiInApi Definitions
280 //*********************************************************************//
281 
MidiInApi(unsigned int queueSizeLimit)282 MidiInApi :: MidiInApi( unsigned int queueSizeLimit )
283   : MidiApi()
284 {
285   // Allocate the MIDI queue.
286   inputData_.queue.ringSize = queueSizeLimit;
287   if ( inputData_.queue.ringSize > 0 )
288     inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ];
289 }
290 
~MidiInApi(void)291 MidiInApi :: ~MidiInApi( void )
292 {
293   // Delete the MIDI queue.
294   if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring;
295 }
296 
setCallback(RtMidiIn::RtMidiCallback callback,void * userData)297 void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData )
298 {
299   if ( inputData_.usingCallback ) {
300     errorString_ = "MidiInApi::setCallback: a callback function is already set!";
301     error( RtMidiError::WARNING, errorString_ );
302     return;
303   }
304 
305   if ( !callback ) {
306     errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";
307     error( RtMidiError::WARNING, errorString_ );
308     return;
309   }
310 
311   inputData_.userCallback = callback;
312   inputData_.userData = userData;
313   inputData_.usingCallback = true;
314 }
315 
cancelCallback()316 void MidiInApi :: cancelCallback()
317 {
318   if ( !inputData_.usingCallback ) {
319     errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";
320     error( RtMidiError::WARNING, errorString_ );
321     return;
322   }
323 
324   inputData_.userCallback = 0;
325   inputData_.userData = 0;
326   inputData_.usingCallback = false;
327 }
328 
ignoreTypes(bool midiSysex,bool midiTime,bool midiSense)329 void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )
330 {
331   inputData_.ignoreFlags = 0;
332   if ( midiSysex ) inputData_.ignoreFlags = 0x01;
333   if ( midiTime ) inputData_.ignoreFlags |= 0x02;
334   if ( midiSense ) inputData_.ignoreFlags |= 0x04;
335 }
336 
getMessage(std::vector<unsigned char> * message)337 double MidiInApi :: getMessage( std::vector<unsigned char> *message )
338 {
339   message->clear();
340 
341   if ( inputData_.usingCallback ) {
342     errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";
343     error( RtMidiError::WARNING, errorString_ );
344     return 0.0;
345   }
346 
347   double timeStamp;
348   if (!inputData_.queue.pop(message, &timeStamp))
349     return 0.0;
350 
351   return timeStamp;
352 }
353 
size(unsigned int * __back,unsigned int * __front)354 unsigned int MidiInApi::MidiQueue::size(unsigned int *__back,
355 					unsigned int *__front)
356 {
357   // Access back/front members exactly once and make stack copies for
358   // size calculation
359   unsigned int _back = back, _front = front, _size;
360   if (_back >= _front)
361     _size = _back - _front;
362   else
363     _size = ringSize - _front + _back;
364 
365   // Return copies of back/front so no new and unsynchronized accesses
366   // to member variables are needed.
367   if (__back) *__back = _back;
368   if (__front) *__front = _front;
369   return _size;
370 }
371 
372 // As long as we haven't reached our queue size limit, push the message.
push(const MidiInApi::MidiMessage & msg)373 bool MidiInApi::MidiQueue::push(const MidiInApi::MidiMessage& msg)
374 {
375   // Local stack copies of front/back
376   unsigned int _back, _front, _size;
377 
378   // Get back/front indexes exactly once and calculate current size
379   _size = size(&_back, &_front);
380 
381   if ( _size < ringSize-1 )
382   {
383     ring[_back] = msg;
384     back = (back+1)%ringSize;
385     return true;
386   }
387 
388   return false;
389 }
390 
pop(std::vector<unsigned char> * msg,double * timeStamp)391 bool MidiInApi::MidiQueue::pop(std::vector<unsigned char> *msg, double* timeStamp)
392 {
393   // Local stack copies of front/back
394   unsigned int _back, _front, _size;
395 
396   // Get back/front indexes exactly once and calculate current size
397   _size = size(&_back, &_front);
398 
399   if (_size == 0)
400     return false;
401 
402   // Copy queued message to the vector pointer argument and then "pop" it.
403   msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() );
404   *timeStamp = ring[_front].timeStamp;
405 
406   // Update front
407   front = (front+1)%ringSize;
408   return true;
409 }
410 
411 //*********************************************************************//
412 //  Common MidiOutApi Definitions
413 //*********************************************************************//
414 
MidiOutApi(void)415 MidiOutApi :: MidiOutApi( void )
416   : MidiApi()
417 {
418 }
419 
~MidiOutApi(void)420 MidiOutApi :: ~MidiOutApi( void )
421 {
422 }
423 
424 // *************************************************** //
425 //
426 // OS/API-specific methods.
427 //
428 // *************************************************** //
429 
430 #if defined(__MACOSX_CORE__)
431 
432 // The CoreMIDI API is based on the use of a callback function for
433 // MIDI input.  We convert the system specific time stamps to delta
434 // time values.
435 
436 // OS-X CoreMIDI header files.
437 #include <CoreMIDI/CoreMIDI.h>
438 #include <CoreAudio/HostTime.h>
439 #include <CoreServices/CoreServices.h>
440 
441 // A structure to hold variables related to the CoreMIDI API
442 // implementation.
443 struct CoreMidiData {
444   MIDIClientRef client;
445   MIDIPortRef port;
446   MIDIEndpointRef endpoint;
447   MIDIEndpointRef destinationId;
448   unsigned long long lastTime;
449   MIDISysexSendRequest sysexreq;
450 };
451 
452 //*********************************************************************//
453 //  API: OS-X
454 //  Class Definitions: MidiInCore
455 //*********************************************************************//
456 
midiInputCallback(const MIDIPacketList * list,void * procRef,void *)457 static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ )
458 {
459   MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef);
460   CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
461 
462   unsigned char status;
463   unsigned short nBytes, iByte, size;
464   unsigned long long time;
465 
466   bool& continueSysex = data->continueSysex;
467   MidiInApi::MidiMessage& message = data->message;
468 
469   const MIDIPacket *packet = &list->packet[0];
470   for ( unsigned int i=0; i<list->numPackets; ++i ) {
471 
472     // My interpretation of the CoreMIDI documentation: all message
473     // types, except sysex, are complete within a packet and there may
474     // be several of them in a single packet.  Sysex messages can be
475     // broken across multiple packets and PacketLists but are bundled
476     // alone within each packet (these packets do not contain other
477     // message types).  If sysex messages are split across multiple
478     // MIDIPacketLists, they must be handled by multiple calls to this
479     // function.
480 
481     nBytes = packet->length;
482     if ( nBytes == 0 ) continue;
483 
484     // Calculate time stamp.
485     if ( data->firstMessage ) {
486       message.timeStamp = 0.0;
487       data->firstMessage = false;
488     }
489     else {
490       time = packet->timeStamp;
491       if ( time == 0 ) { // this happens when receiving asynchronous sysex messages
492         time = AudioGetCurrentHostTime();
493       }
494       time -= apiData->lastTime;
495       time = AudioConvertHostTimeToNanos( time );
496       if ( !continueSysex )
497         message.timeStamp = time * 0.000000001;
498     }
499 
500     // Track whether any non-filtered messages were found in this
501     // packet for timestamp calculation
502     bool foundNonFiltered = false;
503 
504     iByte = 0;
505     if ( continueSysex ) {
506       // We have a continuing, segmented sysex message.
507       if ( !( data->ignoreFlags & 0x01 ) ) {
508         // If we're not ignoring sysex messages, copy the entire packet.
509         for ( unsigned int j=0; j<nBytes; ++j )
510           message.bytes.push_back( packet->data[j] );
511       }
512       continueSysex = packet->data[nBytes-1] != 0xF7;
513 
514       if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) {
515         // If not a continuing sysex message, invoke the user callback function or queue the message.
516         if ( data->usingCallback ) {
517           RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
518           callback( message.timeStamp, &message.bytes, data->userData );
519         }
520         else {
521           // As long as we haven't reached our queue size limit, push the message.
522           if (!data->queue.push(message))
523             std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
524         }
525         message.bytes.clear();
526       }
527     }
528     else {
529       while ( iByte < nBytes ) {
530         size = 0;
531         // We are expecting that the next byte in the packet is a status byte.
532         status = packet->data[iByte];
533         if ( !(status & 0x80) ) break;
534         // Determine the number of bytes in the MIDI message.
535         if ( status < 0xC0 ) size = 3;
536         else if ( status < 0xE0 ) size = 2;
537         else if ( status < 0xF0 ) size = 3;
538         else if ( status == 0xF0 ) {
539           // A MIDI sysex
540           if ( data->ignoreFlags & 0x01 ) {
541             size = 0;
542             iByte = nBytes;
543           }
544           else size = nBytes - iByte;
545           continueSysex = packet->data[nBytes-1] != 0xF7;
546         }
547         else if ( status == 0xF1 ) {
548             // A MIDI time code message
549            if ( data->ignoreFlags & 0x02 ) {
550             size = 0;
551             iByte += 2;
552            }
553            else size = 2;
554         }
555         else if ( status == 0xF2 ) size = 3;
556         else if ( status == 0xF3 ) size = 2;
557         else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {
558           // A MIDI timing tick message and we're ignoring it.
559           size = 0;
560           iByte += 1;
561         }
562         else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {
563           // A MIDI active sensing message and we're ignoring it.
564           size = 0;
565           iByte += 1;
566         }
567         else size = 1;
568 
569         // Copy the MIDI data to our vector.
570         if ( size ) {
571           foundNonFiltered = true;
572           message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );
573           if ( !continueSysex ) {
574             // If not a continuing sysex message, invoke the user callback function or queue the message.
575             if ( data->usingCallback ) {
576               RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
577               callback( message.timeStamp, &message.bytes, data->userData );
578             }
579             else {
580               // As long as we haven't reached our queue size limit, push the message.
581               if (!data->queue.push(message))
582                 std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
583             }
584             message.bytes.clear();
585           }
586           iByte += size;
587         }
588       }
589     }
590 
591     // Save the time of the last non-filtered message
592     if (foundNonFiltered)
593     {
594       apiData->lastTime = packet->timeStamp;
595       if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages
596         apiData->lastTime = AudioGetCurrentHostTime();
597       }
598     }
599 
600     packet = MIDIPacketNext(packet);
601   }
602 }
603 
MidiInCore(const std::string & clientName,unsigned int queueSizeLimit)604 MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
605 {
606   MidiInCore::initialize( clientName );
607 }
608 
~MidiInCore(void)609 MidiInCore :: ~MidiInCore( void )
610 {
611   // Close a connection if it exists.
612   MidiInCore::closePort();
613 
614   // Cleanup.
615   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
616   MIDIClientDispose( data->client );
617   if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
618   delete data;
619 }
620 
initialize(const std::string & clientName)621 void MidiInCore :: initialize( const std::string& clientName )
622 {
623   // Set up our client.
624   MIDIClientRef client;
625   CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
626   OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
627   if ( result != noErr ) {
628     std::ostringstream ost;
629     ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
630     errorString_ = ost.str();
631     error( RtMidiError::DRIVER_ERROR, errorString_ );
632     return;
633   }
634 
635   // Save our api-specific connection information.
636   CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
637   data->client = client;
638   data->endpoint = 0;
639   apiData_ = (void *) data;
640   inputData_.apiData = (void *) data;
641   CFRelease(name);
642 }
643 
openPort(unsigned int portNumber,const std::string & portName)644 void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName )
645 {
646   if ( connected_ ) {
647     errorString_ = "MidiInCore::openPort: a valid connection already exists!";
648     error( RtMidiError::WARNING, errorString_ );
649     return;
650   }
651 
652   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
653   unsigned int nSrc = MIDIGetNumberOfSources();
654   if (nSrc < 1) {
655     errorString_ = "MidiInCore::openPort: no MIDI input sources found!";
656     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
657     return;
658   }
659 
660   if ( portNumber >= nSrc ) {
661     std::ostringstream ost;
662     ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
663     errorString_ = ost.str();
664     error( RtMidiError::INVALID_PARAMETER, errorString_ );
665     return;
666   }
667 
668   MIDIPortRef port;
669   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
670   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
671   OSStatus result = MIDIInputPortCreate( data->client,
672                                          portNameRef,
673                                          midiInputCallback, (void *)&inputData_, &port );
674   CFRelease( portNameRef );
675 
676   if ( result != noErr ) {
677     MIDIClientDispose( data->client );
678     errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
679     error( RtMidiError::DRIVER_ERROR, errorString_ );
680     return;
681   }
682 
683   // Get the desired input source identifier.
684   MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
685   if ( endpoint == 0 ) {
686     MIDIPortDispose( port );
687     MIDIClientDispose( data->client );
688     errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
689     error( RtMidiError::DRIVER_ERROR, errorString_ );
690     return;
691   }
692 
693   // Make the connection.
694   result = MIDIPortConnectSource( port, endpoint, NULL );
695   if ( result != noErr ) {
696     MIDIPortDispose( port );
697     MIDIClientDispose( data->client );
698     errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
699     error( RtMidiError::DRIVER_ERROR, errorString_ );
700     return;
701   }
702 
703   // Save our api-specific port information.
704   data->port = port;
705 
706   connected_ = true;
707 }
708 
openVirtualPort(const std::string & portName)709 void MidiInCore :: openVirtualPort( const std::string &portName )
710 {
711   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
712 
713   // Create a virtual MIDI input destination.
714   MIDIEndpointRef endpoint;
715   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
716   OSStatus result = MIDIDestinationCreate( data->client,
717                                            portNameRef,
718                                            midiInputCallback, (void *)&inputData_, &endpoint );
719   CFRelease( portNameRef );
720 
721   if ( result != noErr ) {
722     errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination.";
723     error( RtMidiError::DRIVER_ERROR, errorString_ );
724     return;
725   }
726 
727   // Save our api-specific connection information.
728   data->endpoint = endpoint;
729 }
730 
closePort(void)731 void MidiInCore :: closePort( void )
732 {
733   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
734 
735   if ( data->endpoint ) {
736     MIDIEndpointDispose( data->endpoint );
737     data->endpoint = 0;
738   }
739 
740   if ( data->port ) {
741     MIDIPortDispose( data->port );
742     data->port = 0;
743   }
744 
745   connected_ = false;
746 }
747 
getPortCount()748 unsigned int MidiInCore :: getPortCount()
749 {
750   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
751   return MIDIGetNumberOfSources();
752 }
753 
754 // This function was submitted by Douglas Casey Tucker and apparently
755 // derived largely from PortMidi.
EndpointName(MIDIEndpointRef endpoint,bool isExternal)756 CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
757 {
758   CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
759   CFStringRef str;
760 
761   // Begin with the endpoint's name.
762   str = NULL;
763   MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );
764   if ( str != NULL ) {
765     CFStringAppend( result, str );
766     CFRelease( str );
767   }
768 
769   MIDIEntityRef entity = 0;
770   MIDIEndpointGetEntity( endpoint, &entity );
771   if ( entity == 0 )
772     // probably virtual
773     return result;
774 
775   if ( CFStringGetLength( result ) == 0 ) {
776     // endpoint name has zero length -- try the entity
777     str = NULL;
778     MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );
779     if ( str != NULL ) {
780       CFStringAppend( result, str );
781       CFRelease( str );
782     }
783   }
784   // now consider the device's name
785   MIDIDeviceRef device = 0;
786   MIDIEntityGetDevice( entity, &device );
787   if ( device == 0 )
788     return result;
789 
790   str = NULL;
791   MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );
792   if ( CFStringGetLength( result ) == 0 ) {
793       CFRelease( result );
794       return str;
795   }
796   if ( str != NULL ) {
797     // if an external device has only one entity, throw away
798     // the endpoint name and just use the device name
799     if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {
800       CFRelease( result );
801       return str;
802     } else {
803       if ( CFStringGetLength( str ) == 0 ) {
804         CFRelease( str );
805         return result;
806       }
807       // does the entity name already start with the device name?
808       // (some drivers do this though they shouldn't)
809       // if so, do not prepend
810         if ( CFStringCompareWithOptions( result, /* endpoint name */
811              str /* device name */,
812              CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {
813         // prepend the device name to the entity name
814         if ( CFStringGetLength( result ) > 0 )
815           CFStringInsert( result, 0, CFSTR(" ") );
816         CFStringInsert( result, 0, str );
817       }
818       CFRelease( str );
819     }
820   }
821   return result;
822 }
823 
824 // This function was submitted by Douglas Casey Tucker and apparently
825 // derived largely from PortMidi.
ConnectedEndpointName(MIDIEndpointRef endpoint)826 static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )
827 {
828   CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
829   CFStringRef str;
830   OSStatus err;
831   int i;
832 
833   // Does the endpoint have connections?
834   CFDataRef connections = NULL;
835   int nConnected = 0;
836   bool anyStrings = false;
837   err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );
838   if ( connections != NULL ) {
839     // It has connections, follow them
840     // Concatenate the names of all connected devices
841     nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);
842     if ( nConnected ) {
843       const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
844       for ( i=0; i<nConnected; ++i, ++pid ) {
845         MIDIUniqueID id = EndianS32_BtoN( *pid );
846         MIDIObjectRef connObject;
847         MIDIObjectType connObjectType;
848         err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );
849         if ( err == noErr ) {
850           if ( connObjectType == kMIDIObjectType_ExternalSource  ||
851               connObjectType == kMIDIObjectType_ExternalDestination ) {
852             // Connected to an external device's endpoint (10.3 and later).
853             str = EndpointName( (MIDIEndpointRef)(connObject), true );
854           } else {
855             // Connected to an external device (10.2) (or something else, catch-
856             str = NULL;
857             MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );
858           }
859           if ( str != NULL ) {
860             if ( anyStrings )
861               CFStringAppend( result, CFSTR(", ") );
862             else anyStrings = true;
863             CFStringAppend( result, str );
864             CFRelease( str );
865           }
866         }
867       }
868     }
869     CFRelease( connections );
870   }
871   if ( anyStrings )
872     return result;
873 
874   CFRelease( result );
875 
876   // Here, either the endpoint had no connections, or we failed to obtain names
877   return EndpointName( endpoint, false );
878 }
879 
getPortName(unsigned int portNumber)880 std::string MidiInCore :: getPortName( unsigned int portNumber )
881 {
882   CFStringRef nameRef;
883   MIDIEndpointRef portRef;
884   char name[128];
885 
886   std::string stringName;
887   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
888   if ( portNumber >= MIDIGetNumberOfSources() ) {
889     std::ostringstream ost;
890     ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
891     errorString_ = ost.str();
892     error( RtMidiError::WARNING, errorString_ );
893     return stringName;
894   }
895 
896   portRef = MIDIGetSource( portNumber );
897   nameRef = ConnectedEndpointName(portRef);
898   CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8);
899   CFRelease( nameRef );
900 
901   return stringName = name;
902 }
903 
904 //*********************************************************************//
905 //  API: OS-X
906 //  Class Definitions: MidiOutCore
907 //*********************************************************************//
908 
MidiOutCore(const std::string & clientName)909 MidiOutCore :: MidiOutCore( const std::string &clientName ) : MidiOutApi()
910 {
911   MidiOutCore::initialize( clientName );
912 }
913 
~MidiOutCore(void)914 MidiOutCore :: ~MidiOutCore( void )
915 {
916   // Close a connection if it exists.
917   MidiOutCore::closePort();
918 
919   // Cleanup.
920   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
921   MIDIClientDispose( data->client );
922   if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
923   delete data;
924 }
925 
initialize(const std::string & clientName)926 void MidiOutCore :: initialize( const std::string& clientName )
927 {
928   // Set up our client.
929   MIDIClientRef client;
930   CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
931   OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
932   if ( result != noErr ) {
933     std::ostringstream ost;
934     ost << "MidiOutCore::initialize: error creating OS-X MIDI client object (" << result << ").";
935     errorString_ = ost.str();
936     error( RtMidiError::DRIVER_ERROR, errorString_ );
937     return;
938   }
939 
940   // Save our api-specific connection information.
941   CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
942   data->client = client;
943   data->endpoint = 0;
944   apiData_ = (void *) data;
945   CFRelease( name );
946 }
947 
getPortCount()948 unsigned int MidiOutCore :: getPortCount()
949 {
950   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
951   return MIDIGetNumberOfDestinations();
952 }
953 
getPortName(unsigned int portNumber)954 std::string MidiOutCore :: getPortName( unsigned int portNumber )
955 {
956   CFStringRef nameRef;
957   MIDIEndpointRef portRef;
958   char name[128];
959 
960   std::string stringName;
961   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
962   if ( portNumber >= MIDIGetNumberOfDestinations() ) {
963     std::ostringstream ost;
964     ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
965     errorString_ = ost.str();
966     error( RtMidiError::WARNING, errorString_ );
967     return stringName;
968   }
969 
970   portRef = MIDIGetDestination( portNumber );
971   nameRef = ConnectedEndpointName(portRef);
972   CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 );
973   CFRelease( nameRef );
974 
975   return stringName = name;
976 }
977 
openPort(unsigned int portNumber,const std::string & portName)978 void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName )
979 {
980   if ( connected_ ) {
981     errorString_ = "MidiOutCore::openPort: a valid connection already exists!";
982     error( RtMidiError::WARNING, errorString_ );
983     return;
984   }
985 
986   CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
987   unsigned int nDest = MIDIGetNumberOfDestinations();
988   if (nDest < 1) {
989     errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!";
990     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
991     return;
992   }
993 
994   if ( portNumber >= nDest ) {
995     std::ostringstream ost;
996     ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
997     errorString_ = ost.str();
998     error( RtMidiError::INVALID_PARAMETER, errorString_ );
999     return;
1000   }
1001 
1002   MIDIPortRef port;
1003   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
1004   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
1005   OSStatus result = MIDIOutputPortCreate( data->client,
1006                                           portNameRef,
1007                                           &port );
1008   CFRelease( portNameRef );
1009   if ( result != noErr ) {
1010     MIDIClientDispose( data->client );
1011     errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
1012     error( RtMidiError::DRIVER_ERROR, errorString_ );
1013     return;
1014   }
1015 
1016   // Get the desired output port identifier.
1017   MIDIEndpointRef destination = MIDIGetDestination( portNumber );
1018   if ( destination == 0 ) {
1019     MIDIPortDispose( port );
1020     MIDIClientDispose( data->client );
1021     errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
1022     error( RtMidiError::DRIVER_ERROR, errorString_ );
1023     return;
1024   }
1025 
1026   // Save our api-specific connection information.
1027   data->port = port;
1028   data->destinationId = destination;
1029   connected_ = true;
1030 }
1031 
closePort(void)1032 void MidiOutCore :: closePort( void )
1033 {
1034   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
1035 
1036   if ( data->endpoint ) {
1037     MIDIEndpointDispose( data->endpoint );
1038     data->endpoint = 0;
1039   }
1040 
1041   if ( data->port ) {
1042     MIDIPortDispose( data->port );
1043     data->port = 0;
1044   }
1045 
1046   connected_ = false;
1047 }
1048 
openVirtualPort(const std::string & portName)1049 void MidiOutCore :: openVirtualPort( const std::string &portName )
1050 {
1051   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
1052 
1053   if ( data->endpoint ) {
1054     errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!";
1055     error( RtMidiError::WARNING, errorString_ );
1056     return;
1057   }
1058 
1059   // Create a virtual MIDI output source.
1060   MIDIEndpointRef endpoint;
1061   CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII );
1062   OSStatus result = MIDISourceCreate( data->client,
1063                                       portNameRef,
1064                                       &endpoint );
1065   CFRelease( portNameRef );
1066 
1067   if ( result != noErr ) {
1068     errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source.";
1069     error( RtMidiError::DRIVER_ERROR, errorString_ );
1070     return;
1071   }
1072 
1073   // Save our api-specific connection information.
1074   data->endpoint = endpoint;
1075 }
1076 
sendMessage(const unsigned char * message,size_t size)1077 void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )
1078 {
1079   // We use the MIDISendSysex() function to asynchronously send sysex
1080   // messages.  Otherwise, we use a single CoreMidi MIDIPacket.
1081   unsigned int nBytes = static_cast<unsigned int> (size);
1082   if ( nBytes == 0 ) {
1083     errorString_ = "MidiOutCore::sendMessage: no data in message argument!";
1084     error( RtMidiError::WARNING, errorString_ );
1085     return;
1086   }
1087 
1088   MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
1089   CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
1090   OSStatus result;
1091 
1092   if ( message[0] != 0xF0 && nBytes > 3 ) {
1093     errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
1094     error( RtMidiError::WARNING, errorString_ );
1095     return;
1096   }
1097 
1098   Byte buffer[nBytes+(sizeof(MIDIPacketList))];
1099   ByteCount listSize = sizeof(buffer);
1100   MIDIPacketList *packetList = (MIDIPacketList*)buffer;
1101   MIDIPacket *packet = MIDIPacketListInit( packetList );
1102 
1103   ByteCount remainingBytes = nBytes;
1104   while (remainingBytes && packet) {
1105     ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket
1106     const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes];
1107     packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr);
1108     remainingBytes -= bytesForPacket;
1109   }
1110 
1111   if ( !packet ) {
1112     errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
1113     error( RtMidiError::DRIVER_ERROR, errorString_ );
1114     return;
1115   }
1116 
1117   // Send to any destinations that may have connected to us.
1118   if ( data->endpoint ) {
1119     result = MIDIReceived( data->endpoint, packetList );
1120     if ( result != noErr ) {
1121       errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
1122       error( RtMidiError::WARNING, errorString_ );
1123     }
1124   }
1125 
1126   // And send to an explicit destination port if we're connected.
1127   if ( connected_ ) {
1128     result = MIDISend( data->port, data->destinationId, packetList );
1129     if ( result != noErr ) {
1130       errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
1131       error( RtMidiError::WARNING, errorString_ );
1132     }
1133   }
1134 }
1135 
1136 #endif  // __MACOSX_CORE__
1137 
1138 
1139 //*********************************************************************//
1140 //  API: LINUX ALSA SEQUENCER
1141 //*********************************************************************//
1142 
1143 // API information found at:
1144 //   - http://www.alsa-project.org/documentation.php#Library
1145 
1146 #if defined(__LINUX_ALSA__)
1147 
1148 // The ALSA Sequencer API is based on the use of a callback function for
1149 // MIDI input.
1150 //
1151 // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
1152 // time stamps and other assorted fixes!!!
1153 
1154 // If you don't need timestamping for incoming MIDI events, define the
1155 // preprocessor definition AVOID_TIMESTAMPING to save resources
1156 // associated with the ALSA sequencer queues.
1157 
1158 #include <pthread.h>
1159 #include <sys/time.h>
1160 
1161 // ALSA header file.
1162 #include <alsa/asoundlib.h>
1163 
1164 // A structure to hold variables related to the ALSA API
1165 // implementation.
1166 struct AlsaMidiData {
1167   snd_seq_t *seq;
1168   unsigned int portNum;
1169   int vport;
1170   snd_seq_port_subscribe_t *subscription;
1171   snd_midi_event_t *coder;
1172   unsigned int bufferSize;
1173   unsigned char *buffer;
1174   pthread_t thread;
1175   pthread_t dummy_thread_id;
1176   snd_seq_real_time_t lastTime;
1177   int queue_id; // an input queue is needed to get timestamped events
1178   int trigger_fds[2];
1179 };
1180 
1181 #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
1182 
1183 //*********************************************************************//
1184 //  API: LINUX ALSA
1185 //  Class Definitions: MidiInAlsa
1186 //*********************************************************************//
1187 
alsaMidiHandler(void * ptr)1188 static void *alsaMidiHandler( void *ptr )
1189 {
1190   MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr);
1191   AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);
1192 
1193   long nBytes;
1194   double time;
1195   bool continueSysex = false;
1196   bool doDecode = false;
1197   MidiInApi::MidiMessage message;
1198   int poll_fd_count;
1199   struct pollfd *poll_fds;
1200 
1201   snd_seq_event_t *ev;
1202   int result;
1203   apiData->bufferSize = 32;
1204   result = snd_midi_event_new( 0, &apiData->coder );
1205   if ( result < 0 ) {
1206     data->doInput = false;
1207     std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n";
1208     return 0;
1209   }
1210   unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );
1211   if ( buffer == NULL ) {
1212     data->doInput = false;
1213     snd_midi_event_free( apiData->coder );
1214     apiData->coder = 0;
1215     std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n";
1216     return 0;
1217   }
1218   snd_midi_event_init( apiData->coder );
1219   snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages
1220 
1221   poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1;
1222   poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd ));
1223   snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN );
1224   poll_fds[0].fd = apiData->trigger_fds[0];
1225   poll_fds[0].events = POLLIN;
1226 
1227   while ( data->doInput ) {
1228 
1229     if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {
1230       // No data pending
1231       if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) {
1232         if ( poll_fds[0].revents & POLLIN ) {
1233           bool dummy;
1234           int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) );
1235           (void) res;
1236         }
1237       }
1238       continue;
1239     }
1240 
1241     // If here, there should be data.
1242     result = snd_seq_event_input( apiData->seq, &ev );
1243     if ( result == -ENOSPC ) {
1244       std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n";
1245       continue;
1246     }
1247     else if ( result <= 0 ) {
1248       std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n";
1249       perror("System reports");
1250       continue;
1251     }
1252 
1253     // This is a bit weird, but we now have to decode an ALSA MIDI
1254     // event (back) into MIDI bytes.  We'll ignore non-MIDI types.
1255     if ( !continueSysex ) message.bytes.clear();
1256 
1257     doDecode = false;
1258     switch ( ev->type ) {
1259 
1260     case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1261 #if defined(__RTMIDI_DEBUG__)
1262       std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n";
1263 #endif
1264       break;
1265 
1266     case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
1267 #if defined(__RTMIDI_DEBUG__)
1268       std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n";
1269       std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"
1270                 << (int) ev->data.connect.sender.port
1271                 << ", dest = " << (int) ev->data.connect.dest.client << ":"
1272                 << (int) ev->data.connect.dest.port
1273                 << std::endl;
1274 #endif
1275       break;
1276 
1277     case SND_SEQ_EVENT_QFRAME: // MIDI time code
1278       if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
1279       break;
1280 
1281     case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick
1282       if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
1283       break;
1284 
1285     case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick
1286       if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
1287       break;
1288 
1289     case SND_SEQ_EVENT_SENSING: // Active sensing
1290       if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;
1291       break;
1292 
1293 		case SND_SEQ_EVENT_SYSEX:
1294       if ( (data->ignoreFlags & 0x01) ) break;
1295       if ( ev->data.ext.len > apiData->bufferSize ) {
1296         apiData->bufferSize = ev->data.ext.len;
1297         free( buffer );
1298         buffer = (unsigned char *) malloc( apiData->bufferSize );
1299         if ( buffer == NULL ) {
1300           data->doInput = false;
1301           std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n";
1302           break;
1303         }
1304       }
1305 
1306     default:
1307       doDecode = true;
1308     }
1309 
1310     if ( doDecode ) {
1311 
1312       nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
1313       if ( nBytes > 0 ) {
1314         // The ALSA sequencer has a maximum buffer size for MIDI sysex
1315         // events of 256 bytes.  If a device sends sysex messages larger
1316         // than this, they are segmented into 256 byte chunks.  So,
1317         // we'll watch for this and concatenate sysex chunks into a
1318         // single sysex message if necessary.
1319         if ( !continueSysex )
1320           message.bytes.assign( buffer, &buffer[nBytes] );
1321         else
1322           message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
1323 
1324         continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
1325         if ( !continueSysex ) {
1326 
1327           // Calculate the time stamp:
1328           message.timeStamp = 0.0;
1329 
1330           // Method 1: Use the system time.
1331           //(void)gettimeofday(&tv, (struct timezone *)NULL);
1332           //time = (tv.tv_sec * 1000000) + tv.tv_usec;
1333 
1334           // Method 2: Use the ALSA sequencer event time data.
1335           // (thanks to Pedro Lopez-Cabanillas!).
1336 
1337           // Using method from:
1338           // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
1339 
1340           // Perform the carry for the later subtraction by updating y.
1341           snd_seq_real_time_t &x(ev->time.time);
1342           snd_seq_real_time_t &y(apiData->lastTime);
1343           if (x.tv_nsec < y.tv_nsec) {
1344               int nsec = (y.tv_nsec - x.tv_nsec) / 1000000000 + 1;
1345               y.tv_nsec -= 1000000000 * nsec;
1346               y.tv_sec += nsec;
1347           }
1348           if (x.tv_nsec - y.tv_nsec > 1000000000) {
1349               int nsec = (x.tv_nsec - y.tv_nsec) / 1000000000;
1350               y.tv_nsec += 1000000000 * nsec;
1351               y.tv_sec -= nsec;
1352           }
1353 
1354           // Compute the time difference.
1355           time = x.tv_sec - y.tv_sec + (x.tv_nsec - y.tv_nsec)*1e-9;
1356 
1357           apiData->lastTime = ev->time.time;
1358 
1359           if ( data->firstMessage == true )
1360             data->firstMessage = false;
1361           else
1362             message.timeStamp = time;
1363         }
1364         else {
1365 #if defined(__RTMIDI_DEBUG__)
1366           std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
1367 #endif
1368         }
1369       }
1370     }
1371 
1372     snd_seq_free_event( ev );
1373     if ( message.bytes.size() == 0 || continueSysex ) continue;
1374 
1375     if ( data->usingCallback ) {
1376       RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
1377       callback( message.timeStamp, &message.bytes, data->userData );
1378     }
1379     else {
1380       // As long as we haven't reached our queue size limit, push the message.
1381       if (!data->queue.push(message))
1382         std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n";
1383     }
1384   }
1385 
1386   if ( buffer ) free( buffer );
1387   snd_midi_event_free( apiData->coder );
1388   apiData->coder = 0;
1389   apiData->thread = apiData->dummy_thread_id;
1390   return 0;
1391 }
1392 
MidiInAlsa(const std::string & clientName,unsigned int queueSizeLimit)1393 MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
1394 {
1395   MidiInAlsa::initialize( clientName );
1396 }
1397 
~MidiInAlsa()1398 MidiInAlsa :: ~MidiInAlsa()
1399 {
1400   // Close a connection if it exists.
1401   MidiInAlsa::closePort();
1402 
1403   // Shutdown the input thread.
1404   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1405   if ( inputData_.doInput ) {
1406     inputData_.doInput = false;
1407     int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );
1408     (void) res;
1409     if ( !pthread_equal(data->thread, data->dummy_thread_id) )
1410       pthread_join( data->thread, NULL );
1411   }
1412 
1413   // Cleanup.
1414   close ( data->trigger_fds[0] );
1415   close ( data->trigger_fds[1] );
1416   if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
1417 #ifndef AVOID_TIMESTAMPING
1418   snd_seq_free_queue( data->seq, data->queue_id );
1419 #endif
1420   snd_seq_close( data->seq );
1421   delete data;
1422 }
1423 
initialize(const std::string & clientName)1424 void MidiInAlsa :: initialize( const std::string& clientName )
1425 {
1426   // Set up the ALSA sequencer client.
1427   snd_seq_t *seq;
1428   int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
1429   if ( result < 0 ) {
1430     errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object.";
1431     error( RtMidiError::DRIVER_ERROR, errorString_ );
1432     return;
1433   }
1434 
1435   // Set client name.
1436   snd_seq_set_client_name( seq, clientName.c_str() );
1437 
1438   // Save our api-specific connection information.
1439   AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
1440   data->seq = seq;
1441   data->portNum = -1;
1442   data->vport = -1;
1443   data->subscription = 0;
1444   data->dummy_thread_id = pthread_self();
1445   data->thread = data->dummy_thread_id;
1446   data->trigger_fds[0] = -1;
1447   data->trigger_fds[1] = -1;
1448   apiData_ = (void *) data;
1449   inputData_.apiData = (void *) data;
1450 
1451    if ( pipe(data->trigger_fds) == -1 ) {
1452     errorString_ = "MidiInAlsa::initialize: error creating pipe objects.";
1453     error( RtMidiError::DRIVER_ERROR, errorString_ );
1454     return;
1455   }
1456 
1457   // Create the input queue
1458 #ifndef AVOID_TIMESTAMPING
1459   data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue");
1460   // Set arbitrary tempo (mm=100) and resolution (240)
1461   snd_seq_queue_tempo_t *qtempo;
1462   snd_seq_queue_tempo_alloca(&qtempo);
1463   snd_seq_queue_tempo_set_tempo(qtempo, 600000);
1464   snd_seq_queue_tempo_set_ppq(qtempo, 240);
1465   snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo);
1466   snd_seq_drain_output(data->seq);
1467 #endif
1468 }
1469 
1470 // This function is used to count or get the pinfo structure for a given port number.
portInfo(snd_seq_t * seq,snd_seq_port_info_t * pinfo,unsigned int type,int portNumber)1471 unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )
1472 {
1473   snd_seq_client_info_t *cinfo;
1474   int client;
1475   int count = 0;
1476   snd_seq_client_info_alloca( &cinfo );
1477 
1478   snd_seq_client_info_set_client( cinfo, -1 );
1479   while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {
1480     client = snd_seq_client_info_get_client( cinfo );
1481     if ( client == 0 ) continue;
1482     // Reset query info
1483     snd_seq_port_info_set_client( pinfo, client );
1484     snd_seq_port_info_set_port( pinfo, -1 );
1485     while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
1486       unsigned int atyp = snd_seq_port_info_get_type( pinfo );
1487       if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&
1488            ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) &&
1489            ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue;
1490 
1491       unsigned int caps = snd_seq_port_info_get_capability( pinfo );
1492       if ( ( caps & type ) != type ) continue;
1493       if ( count == portNumber ) return 1;
1494       ++count;
1495     }
1496   }
1497 
1498   // If a negative portNumber was used, return the port count.
1499   if ( portNumber < 0 ) return count;
1500   return 0;
1501 }
1502 
getPortCount()1503 unsigned int MidiInAlsa :: getPortCount()
1504 {
1505   snd_seq_port_info_t *pinfo;
1506   snd_seq_port_info_alloca( &pinfo );
1507 
1508   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1509   return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );
1510 }
1511 
getPortName(unsigned int portNumber)1512 std::string MidiInAlsa :: getPortName( unsigned int portNumber )
1513 {
1514   snd_seq_client_info_t *cinfo;
1515   snd_seq_port_info_t *pinfo;
1516   snd_seq_client_info_alloca( &cinfo );
1517   snd_seq_port_info_alloca( &pinfo );
1518 
1519   std::string stringName;
1520   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1521   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
1522     int cnum = snd_seq_port_info_get_client( pinfo );
1523     snd_seq_get_any_client_info( data->seq, cnum, cinfo );
1524     std::ostringstream os;
1525     os << snd_seq_client_info_get_name( cinfo );
1526     os << ":";
1527     os << snd_seq_port_info_get_name( pinfo );
1528     os << " ";                                    // These lines added to make sure devices are listed
1529     os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names
1530     os << ":";
1531     os << snd_seq_port_info_get_port( pinfo );
1532     stringName = os.str();
1533     return stringName;
1534   }
1535 
1536   // If we get here, we didn't find a match.
1537   errorString_ = "MidiInAlsa::getPortName: error looking for port name!";
1538   error( RtMidiError::WARNING, errorString_ );
1539   return stringName;
1540 }
1541 
openPort(unsigned int portNumber,const std::string & portName)1542 void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName )
1543 {
1544   if ( connected_ ) {
1545     errorString_ = "MidiInAlsa::openPort: a valid connection already exists!";
1546     error( RtMidiError::WARNING, errorString_ );
1547     return;
1548   }
1549 
1550   unsigned int nSrc = this->getPortCount();
1551   if ( nSrc < 1 ) {
1552     errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!";
1553     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
1554     return;
1555   }
1556 
1557   snd_seq_port_info_t *src_pinfo;
1558   snd_seq_port_info_alloca( &src_pinfo );
1559   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1560   if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {
1561     std::ostringstream ost;
1562     ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
1563     errorString_ = ost.str();
1564     error( RtMidiError::INVALID_PARAMETER, errorString_ );
1565     return;
1566   }
1567 
1568   snd_seq_addr_t sender, receiver;
1569   sender.client = snd_seq_port_info_get_client( src_pinfo );
1570   sender.port = snd_seq_port_info_get_port( src_pinfo );
1571   receiver.client = snd_seq_client_id( data->seq );
1572 
1573   snd_seq_port_info_t *pinfo;
1574   snd_seq_port_info_alloca( &pinfo );
1575   if ( data->vport < 0 ) {
1576     snd_seq_port_info_set_client( pinfo, 0 );
1577     snd_seq_port_info_set_port( pinfo, 0 );
1578     snd_seq_port_info_set_capability( pinfo,
1579                                       SND_SEQ_PORT_CAP_WRITE |
1580                                       SND_SEQ_PORT_CAP_SUBS_WRITE );
1581     snd_seq_port_info_set_type( pinfo,
1582                                 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
1583                                 SND_SEQ_PORT_TYPE_APPLICATION );
1584     snd_seq_port_info_set_midi_channels(pinfo, 16);
1585 #ifndef AVOID_TIMESTAMPING
1586     snd_seq_port_info_set_timestamping(pinfo, 1);
1587     snd_seq_port_info_set_timestamp_real(pinfo, 1);
1588     snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
1589 #endif
1590     snd_seq_port_info_set_name(pinfo,  portName.c_str() );
1591     data->vport = snd_seq_create_port(data->seq, pinfo);
1592 
1593     if ( data->vport < 0 ) {
1594       errorString_ = "MidiInAlsa::openPort: ALSA error creating input port.";
1595       error( RtMidiError::DRIVER_ERROR, errorString_ );
1596       return;
1597     }
1598     data->vport = snd_seq_port_info_get_port(pinfo);
1599   }
1600 
1601   receiver.port = data->vport;
1602 
1603   if ( !data->subscription ) {
1604     // Make subscription
1605     if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {
1606       errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription.";
1607       error( RtMidiError::DRIVER_ERROR, errorString_ );
1608       return;
1609     }
1610     snd_seq_port_subscribe_set_sender(data->subscription, &sender);
1611     snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
1612     if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
1613       snd_seq_port_subscribe_free( data->subscription );
1614       data->subscription = 0;
1615       errorString_ = "MidiInAlsa::openPort: ALSA error making port connection.";
1616       error( RtMidiError::DRIVER_ERROR, errorString_ );
1617       return;
1618     }
1619   }
1620 
1621   if ( inputData_.doInput == false ) {
1622     // Start the input queue
1623 #ifndef AVOID_TIMESTAMPING
1624     snd_seq_start_queue( data->seq, data->queue_id, NULL );
1625     snd_seq_drain_output( data->seq );
1626 #endif
1627     // Start our MIDI input thread.
1628     pthread_attr_t attr;
1629     pthread_attr_init(&attr);
1630     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1631     pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
1632 
1633     inputData_.doInput = true;
1634     int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
1635     pthread_attr_destroy(&attr);
1636     if ( err ) {
1637       snd_seq_unsubscribe_port( data->seq, data->subscription );
1638       snd_seq_port_subscribe_free( data->subscription );
1639       data->subscription = 0;
1640       inputData_.doInput = false;
1641       errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
1642       error( RtMidiError::THREAD_ERROR, errorString_ );
1643       return;
1644     }
1645   }
1646 
1647   connected_ = true;
1648 }
1649 
openVirtualPort(const std::string & portName)1650 void MidiInAlsa :: openVirtualPort( const std::string &portName )
1651 {
1652   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1653   if ( data->vport < 0 ) {
1654     snd_seq_port_info_t *pinfo;
1655     snd_seq_port_info_alloca( &pinfo );
1656     snd_seq_port_info_set_capability( pinfo,
1657 				      SND_SEQ_PORT_CAP_WRITE |
1658 				      SND_SEQ_PORT_CAP_SUBS_WRITE );
1659     snd_seq_port_info_set_type( pinfo,
1660 				SND_SEQ_PORT_TYPE_MIDI_GENERIC |
1661 				SND_SEQ_PORT_TYPE_APPLICATION );
1662     snd_seq_port_info_set_midi_channels(pinfo, 16);
1663 #ifndef AVOID_TIMESTAMPING
1664     snd_seq_port_info_set_timestamping(pinfo, 1);
1665     snd_seq_port_info_set_timestamp_real(pinfo, 1);
1666     snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
1667 #endif
1668     snd_seq_port_info_set_name(pinfo, portName.c_str());
1669     data->vport = snd_seq_create_port(data->seq, pinfo);
1670 
1671     if ( data->vport < 0 ) {
1672       errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port.";
1673       error( RtMidiError::DRIVER_ERROR, errorString_ );
1674       return;
1675     }
1676     data->vport = snd_seq_port_info_get_port(pinfo);
1677   }
1678 
1679   if ( inputData_.doInput == false ) {
1680     // Wait for old thread to stop, if still running
1681     if ( !pthread_equal(data->thread, data->dummy_thread_id) )
1682       pthread_join( data->thread, NULL );
1683 
1684     // Start the input queue
1685 #ifndef AVOID_TIMESTAMPING
1686     snd_seq_start_queue( data->seq, data->queue_id, NULL );
1687     snd_seq_drain_output( data->seq );
1688 #endif
1689     // Start our MIDI input thread.
1690     pthread_attr_t attr;
1691     pthread_attr_init(&attr);
1692     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1693     pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
1694 
1695     inputData_.doInput = true;
1696     int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
1697     pthread_attr_destroy(&attr);
1698     if ( err ) {
1699       if ( data->subscription ) {
1700         snd_seq_unsubscribe_port( data->seq, data->subscription );
1701         snd_seq_port_subscribe_free( data->subscription );
1702         data->subscription = 0;
1703       }
1704       inputData_.doInput = false;
1705       errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
1706       error( RtMidiError::THREAD_ERROR, errorString_ );
1707       return;
1708     }
1709   }
1710 }
1711 
closePort(void)1712 void MidiInAlsa :: closePort( void )
1713 {
1714   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1715 
1716   if ( connected_ ) {
1717     if ( data->subscription ) {
1718       snd_seq_unsubscribe_port( data->seq, data->subscription );
1719       snd_seq_port_subscribe_free( data->subscription );
1720       data->subscription = 0;
1721     }
1722     // Stop the input queue
1723 #ifndef AVOID_TIMESTAMPING
1724     snd_seq_stop_queue( data->seq, data->queue_id, NULL );
1725     snd_seq_drain_output( data->seq );
1726 #endif
1727     connected_ = false;
1728   }
1729 
1730   // Stop thread to avoid triggering the callback, while the port is intended to be closed
1731   if ( inputData_.doInput ) {
1732     inputData_.doInput = false;
1733     int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );
1734     (void) res;
1735     if ( !pthread_equal(data->thread, data->dummy_thread_id) )
1736       pthread_join( data->thread, NULL );
1737   }
1738 }
1739 
1740 //*********************************************************************//
1741 //  API: LINUX ALSA
1742 //  Class Definitions: MidiOutAlsa
1743 //*********************************************************************//
1744 
MidiOutAlsa(const std::string & clientName)1745 MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi()
1746 {
1747   MidiOutAlsa::initialize( clientName );
1748 }
1749 
~MidiOutAlsa()1750 MidiOutAlsa :: ~MidiOutAlsa()
1751 {
1752   // Close a connection if it exists.
1753   MidiOutAlsa::closePort();
1754 
1755   // Cleanup.
1756   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1757   if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
1758   if ( data->coder ) snd_midi_event_free( data->coder );
1759   if ( data->buffer ) free( data->buffer );
1760   snd_seq_close( data->seq );
1761   delete data;
1762 }
1763 
initialize(const std::string & clientName)1764 void MidiOutAlsa :: initialize( const std::string& clientName )
1765 {
1766   // Set up the ALSA sequencer client.
1767   snd_seq_t *seq;
1768   int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
1769   if ( result1 < 0 ) {
1770     errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object.";
1771     error( RtMidiError::DRIVER_ERROR, errorString_ );
1772     return;
1773 	}
1774 
1775   // Set client name.
1776   snd_seq_set_client_name( seq, clientName.c_str() );
1777 
1778   // Save our api-specific connection information.
1779   AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
1780   data->seq = seq;
1781   data->portNum = -1;
1782   data->vport = -1;
1783   data->bufferSize = 32;
1784   data->coder = 0;
1785   data->buffer = 0;
1786   int result = snd_midi_event_new( data->bufferSize, &data->coder );
1787   if ( result < 0 ) {
1788     delete data;
1789     errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n";
1790     error( RtMidiError::DRIVER_ERROR, errorString_ );
1791     return;
1792   }
1793   data->buffer = (unsigned char *) malloc( data->bufferSize );
1794   if ( data->buffer == NULL ) {
1795     delete data;
1796     errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
1797     error( RtMidiError::MEMORY_ERROR, errorString_ );
1798     return;
1799   }
1800   snd_midi_event_init( data->coder );
1801   apiData_ = (void *) data;
1802 }
1803 
getPortCount()1804 unsigned int MidiOutAlsa :: getPortCount()
1805 {
1806 	snd_seq_port_info_t *pinfo;
1807 	snd_seq_port_info_alloca( &pinfo );
1808 
1809   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1810   return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );
1811 }
1812 
getPortName(unsigned int portNumber)1813 std::string MidiOutAlsa :: getPortName( unsigned int portNumber )
1814 {
1815   snd_seq_client_info_t *cinfo;
1816   snd_seq_port_info_t *pinfo;
1817   snd_seq_client_info_alloca( &cinfo );
1818   snd_seq_port_info_alloca( &pinfo );
1819 
1820   std::string stringName;
1821   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1822   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
1823     int cnum = snd_seq_port_info_get_client(pinfo);
1824     snd_seq_get_any_client_info( data->seq, cnum, cinfo );
1825     std::ostringstream os;
1826     os << snd_seq_client_info_get_name(cinfo);
1827     os << ":";
1828     os << snd_seq_port_info_get_name( pinfo );
1829     os << " ";                                    // These lines added to make sure devices are listed
1830     os << snd_seq_port_info_get_client( pinfo );  // with full portnames added to ensure individual device names
1831     os << ":";
1832     os << snd_seq_port_info_get_port(pinfo);
1833     stringName = os.str();
1834     return stringName;
1835   }
1836 
1837   // If we get here, we didn't find a match.
1838   errorString_ = "MidiOutAlsa::getPortName: error looking for port name!";
1839   error( RtMidiError::WARNING, errorString_ );
1840   return stringName;
1841 }
1842 
openPort(unsigned int portNumber,const std::string & portName)1843 void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName )
1844 {
1845   if ( connected_ ) {
1846     errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!";
1847     error( RtMidiError::WARNING, errorString_ );
1848     return;
1849   }
1850 
1851   unsigned int nSrc = this->getPortCount();
1852   if (nSrc < 1) {
1853     errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!";
1854     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
1855     return;
1856   }
1857 
1858 	snd_seq_port_info_t *pinfo;
1859 	snd_seq_port_info_alloca( &pinfo );
1860   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1861   if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {
1862     std::ostringstream ost;
1863     ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
1864     errorString_ = ost.str();
1865     error( RtMidiError::INVALID_PARAMETER, errorString_ );
1866     return;
1867   }
1868 
1869   snd_seq_addr_t sender, receiver;
1870   receiver.client = snd_seq_port_info_get_client( pinfo );
1871   receiver.port = snd_seq_port_info_get_port( pinfo );
1872   sender.client = snd_seq_client_id( data->seq );
1873 
1874   if ( data->vport < 0 ) {
1875     data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
1876                                               SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
1877                                               SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
1878     if ( data->vport < 0 ) {
1879       errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port.";
1880       error( RtMidiError::DRIVER_ERROR, errorString_ );
1881       return;
1882     }
1883   }
1884 
1885   sender.port = data->vport;
1886 
1887   // Make subscription
1888   if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {
1889     snd_seq_port_subscribe_free( data->subscription );
1890     errorString_ = "MidiOutAlsa::openPort: error allocating port subscription.";
1891     error( RtMidiError::DRIVER_ERROR, errorString_ );
1892     return;
1893   }
1894   snd_seq_port_subscribe_set_sender(data->subscription, &sender);
1895   snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
1896   snd_seq_port_subscribe_set_time_update(data->subscription, 1);
1897   snd_seq_port_subscribe_set_time_real(data->subscription, 1);
1898   if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
1899     snd_seq_port_subscribe_free( data->subscription );
1900     errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection.";
1901     error( RtMidiError::DRIVER_ERROR, errorString_ );
1902     return;
1903   }
1904 
1905   connected_ = true;
1906 }
1907 
closePort(void)1908 void MidiOutAlsa :: closePort( void )
1909 {
1910   if ( connected_ ) {
1911     AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1912     snd_seq_unsubscribe_port( data->seq, data->subscription );
1913     snd_seq_port_subscribe_free( data->subscription );
1914     data->subscription = 0;
1915     connected_ = false;
1916   }
1917 }
1918 
openVirtualPort(const std::string & portName)1919 void MidiOutAlsa :: openVirtualPort( const std::string &portName )
1920 {
1921   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1922   if ( data->vport < 0 ) {
1923     data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
1924                                               SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
1925                                               SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
1926 
1927     if ( data->vport < 0 ) {
1928       errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port.";
1929       error( RtMidiError::DRIVER_ERROR, errorString_ );
1930     }
1931   }
1932 }
1933 
sendMessage(const unsigned char * message,size_t size)1934 void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
1935 {
1936   int result;
1937   AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
1938   unsigned int nBytes = static_cast<unsigned int> (size);
1939   if ( nBytes > data->bufferSize ) {
1940     data->bufferSize = nBytes;
1941     result = snd_midi_event_resize_buffer ( data->coder, nBytes);
1942     if ( result != 0 ) {
1943       errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer.";
1944       error( RtMidiError::DRIVER_ERROR, errorString_ );
1945       return;
1946     }
1947     free (data->buffer);
1948     data->buffer = (unsigned char *) malloc( data->bufferSize );
1949     if ( data->buffer == NULL ) {
1950     errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
1951     error( RtMidiError::MEMORY_ERROR, errorString_ );
1952     return;
1953     }
1954   }
1955 
1956   snd_seq_event_t ev;
1957   snd_seq_ev_clear(&ev);
1958   snd_seq_ev_set_source(&ev, data->vport);
1959   snd_seq_ev_set_subs(&ev);
1960   snd_seq_ev_set_direct(&ev);
1961   for ( unsigned int i=0; i<nBytes; ++i ) data->buffer[i] = message[i];
1962   result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
1963   if ( result < (int)nBytes ) {
1964     errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
1965     error( RtMidiError::WARNING, errorString_ );
1966     return;
1967   }
1968 
1969   // Send the event.
1970   result = snd_seq_event_output(data->seq, &ev);
1971   if ( result < 0 ) {
1972     errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port.";
1973     error( RtMidiError::WARNING, errorString_ );
1974     return;
1975   }
1976   snd_seq_drain_output(data->seq);
1977 }
1978 
1979 #endif // __LINUX_ALSA__
1980 
1981 
1982 //*********************************************************************//
1983 //  API: Windows Multimedia Library (MM)
1984 //*********************************************************************//
1985 
1986 // API information deciphered from:
1987 //  - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
1988 
1989 // Thanks to Jean-Baptiste Berruchon for the sysex code.
1990 
1991 #if defined(__WINDOWS_MM__)
1992 
1993 // The Windows MM API is based on the use of a callback function for
1994 // MIDI input.  We convert the system specific time stamps to delta
1995 // time values.
1996 
1997 // Windows MM MIDI header files.
1998 #include <windows.h>
1999 #include <mmsystem.h>
2000 
2001 // Convert a null-terminated wide string or ANSI-encoded string to UTF-8.
ConvertToUTF8(const TCHAR * str)2002 static std::string ConvertToUTF8(const TCHAR *str)
2003 {
2004   std::string u8str;
2005   const WCHAR *wstr = L"";
2006 #if defined( UNICODE ) || defined( _UNICODE )
2007   wstr = str;
2008 #else
2009   // Convert from ANSI encoding to wide string
2010   int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
2011   std::wstring wstrtemp;
2012   if ( wlength )
2013   {
2014     wstrtemp.assign( wlength - 1, 0 );
2015     MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength );
2016     wstr = &wstrtemp[0];
2017   }
2018 #endif
2019   // Convert from wide string to UTF-8
2020   int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL );
2021   if ( length )
2022   {
2023     u8str.assign( length - 1, 0 );
2024     length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL );
2025   }
2026   return u8str;
2027 }
2028 
2029 #define  RT_SYSEX_BUFFER_SIZE 1024
2030 #define  RT_SYSEX_BUFFER_COUNT 4
2031 
2032 // A structure to hold variables related to the CoreMIDI API
2033 // implementation.
2034 struct WinMidiData {
2035   HMIDIIN inHandle;    // Handle to Midi Input Device
2036   HMIDIOUT outHandle;  // Handle to Midi Output Device
2037   DWORD lastTime;
2038   MidiInApi::MidiMessage message;
2039   LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];
2040   CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
2041 };
2042 
2043 //*********************************************************************//
2044 //  API: Windows MM
2045 //  Class Definitions: MidiInWinMM
2046 //*********************************************************************//
2047 
midiInputCallback(HMIDIIN,UINT inputStatus,DWORD_PTR instancePtr,DWORD_PTR midiMessage,DWORD timestamp)2048 static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/,
2049                                         UINT inputStatus,
2050                                         DWORD_PTR instancePtr,
2051                                         DWORD_PTR midiMessage,
2052                                         DWORD timestamp )
2053 {
2054   if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;
2055 
2056   //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr);
2057   MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr;
2058   WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);
2059 
2060   // Calculate time stamp.
2061   if ( data->firstMessage == true ) {
2062     apiData->message.timeStamp = 0.0;
2063     data->firstMessage = false;
2064   }
2065   else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;
2066 
2067   if ( inputStatus == MIM_DATA ) { // Channel or system message
2068 
2069     // Make sure the first byte is a status byte.
2070     unsigned char status = (unsigned char) (midiMessage & 0x000000FF);
2071     if ( !(status & 0x80) ) return;
2072 
2073     // Determine the number of bytes in the MIDI message.
2074     unsigned short nBytes = 1;
2075     if ( status < 0xC0 ) nBytes = 3;
2076     else if ( status < 0xE0 ) nBytes = 2;
2077     else if ( status < 0xF0 ) nBytes = 3;
2078     else if ( status == 0xF1 ) {
2079       if ( data->ignoreFlags & 0x02 ) return;
2080       else nBytes = 2;
2081     }
2082     else if ( status == 0xF2 ) nBytes = 3;
2083     else if ( status == 0xF3 ) nBytes = 2;
2084     else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {
2085       // A MIDI timing tick message and we're ignoring it.
2086       return;
2087     }
2088     else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
2089       // A MIDI active sensing message and we're ignoring it.
2090       return;
2091     }
2092 
2093     // Copy bytes to our MIDI message.
2094     unsigned char *ptr = (unsigned char *) &midiMessage;
2095     for ( int i=0; i<nBytes; ++i ) apiData->message.bytes.push_back( *ptr++ );
2096   }
2097   else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )
2098     MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
2099     if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) {
2100       // Sysex message and we're not ignoring it
2101       for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i )
2102         apiData->message.bytes.push_back( sysex->lpData[i] );
2103     }
2104 
2105     // The WinMM API requires that the sysex buffer be requeued after
2106     // input of each sysex message.  Even if we are ignoring sysex
2107     // messages, we still need to requeue the buffer in case the user
2108     // decides to not ignore sysex messages in the future.  However,
2109     // it seems that WinMM calls this function with an empty sysex
2110     // buffer when an application closes and in this case, we should
2111     // avoid requeueing it, else the computer suddenly reboots after
2112     // one or two minutes.
2113     if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {
2114       //if ( sysex->dwBytesRecorded > 0 ) {
2115       EnterCriticalSection( &(apiData->_mutex) );
2116       MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );
2117       LeaveCriticalSection( &(apiData->_mutex) );
2118       if ( result != MMSYSERR_NOERROR )
2119         std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
2120 
2121       if ( data->ignoreFlags & 0x01 ) return;
2122     }
2123     else return;
2124   }
2125 
2126   // Save the time of the last non-filtered message
2127   apiData->lastTime = timestamp;
2128 
2129   if ( data->usingCallback ) {
2130     RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
2131     callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );
2132   }
2133   else {
2134     // As long as we haven't reached our queue size limit, push the message.
2135     if (!data->queue.push(apiData->message))
2136       std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n";
2137   }
2138 
2139   // Clear the vector for the next input message.
2140   apiData->message.bytes.clear();
2141 }
2142 
MidiInWinMM(const std::string & clientName,unsigned int queueSizeLimit)2143 MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
2144 {
2145   MidiInWinMM::initialize( clientName );
2146 }
2147 
~MidiInWinMM()2148 MidiInWinMM :: ~MidiInWinMM()
2149 {
2150   // Close a connection if it exists.
2151   MidiInWinMM::closePort();
2152 
2153   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2154   DeleteCriticalSection( &(data->_mutex) );
2155 
2156   // Cleanup.
2157   delete data;
2158 }
2159 
initialize(const std::string &)2160 void MidiInWinMM :: initialize( const std::string& /*clientName*/ )
2161 {
2162   // We'll issue a warning here if no devices are available but not
2163   // throw an error since the user can plugin something later.
2164   unsigned int nDevices = midiInGetNumDevs();
2165   if ( nDevices == 0 ) {
2166     errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available.";
2167     error( RtMidiError::WARNING, errorString_ );
2168   }
2169 
2170   // Save our api-specific connection information.
2171   WinMidiData *data = (WinMidiData *) new WinMidiData;
2172   apiData_ = (void *) data;
2173   inputData_.apiData = (void *) data;
2174   data->message.bytes.clear();  // needs to be empty for first input message
2175 
2176   if ( !InitializeCriticalSectionAndSpinCount(&(data->_mutex), 0x00000400) ) {
2177     errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed.";
2178     error( RtMidiError::WARNING, errorString_ );
2179   }
2180 }
2181 
openPort(unsigned int portNumber,const std::string &)2182 void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ )
2183 {
2184   if ( connected_ ) {
2185     errorString_ = "MidiInWinMM::openPort: a valid connection already exists!";
2186     error( RtMidiError::WARNING, errorString_ );
2187     return;
2188   }
2189 
2190   unsigned int nDevices = midiInGetNumDevs();
2191   if (nDevices == 0) {
2192     errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!";
2193     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
2194     return;
2195   }
2196 
2197   if ( portNumber >= nDevices ) {
2198     std::ostringstream ost;
2199     ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
2200     errorString_ = ost.str();
2201     error( RtMidiError::INVALID_PARAMETER, errorString_ );
2202     return;
2203   }
2204 
2205   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2206   MMRESULT result = midiInOpen( &data->inHandle,
2207                                 portNumber,
2208                                 (DWORD_PTR)&midiInputCallback,
2209                                 (DWORD_PTR)&inputData_,
2210                                 CALLBACK_FUNCTION );
2211   if ( result != MMSYSERR_NOERROR ) {
2212     errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port.";
2213     error( RtMidiError::DRIVER_ERROR, errorString_ );
2214     return;
2215   }
2216 
2217   // Allocate and init the sysex buffers.
2218   for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
2219     data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
2220     data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
2221     data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
2222     data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
2223     data->sysexBuffer[i]->dwFlags = 0;
2224 
2225     result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
2226     if ( result != MMSYSERR_NOERROR ) {
2227       midiInClose( data->inHandle );
2228       data->inHandle = 0;
2229       errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
2230       error( RtMidiError::DRIVER_ERROR, errorString_ );
2231       return;
2232     }
2233 
2234     // Register the buffer.
2235     result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
2236     if ( result != MMSYSERR_NOERROR ) {
2237       midiInClose( data->inHandle );
2238       data->inHandle = 0;
2239       errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer).";
2240       error( RtMidiError::DRIVER_ERROR, errorString_ );
2241       return;
2242     }
2243   }
2244 
2245   result = midiInStart( data->inHandle );
2246   if ( result != MMSYSERR_NOERROR ) {
2247     midiInClose( data->inHandle );
2248     data->inHandle = 0;
2249     errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port.";
2250     error( RtMidiError::DRIVER_ERROR, errorString_ );
2251     return;
2252   }
2253 
2254   connected_ = true;
2255 }
2256 
openVirtualPort(const std::string &)2257 void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ )
2258 {
2259   // This function cannot be implemented for the Windows MM MIDI API.
2260   errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
2261   error( RtMidiError::WARNING, errorString_ );
2262 }
2263 
closePort(void)2264 void MidiInWinMM :: closePort( void )
2265 {
2266   if ( connected_ ) {
2267     WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2268     EnterCriticalSection( &(data->_mutex) );
2269     midiInReset( data->inHandle );
2270     midiInStop( data->inHandle );
2271 
2272     for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
2273       int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
2274       delete [] data->sysexBuffer[i]->lpData;
2275       delete [] data->sysexBuffer[i];
2276       if ( result != MMSYSERR_NOERROR ) {
2277         midiInClose( data->inHandle );
2278         data->inHandle = 0;
2279         errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
2280         error( RtMidiError::DRIVER_ERROR, errorString_ );
2281         return;
2282       }
2283     }
2284 
2285     midiInClose( data->inHandle );
2286     data->inHandle = 0;
2287     connected_ = false;
2288     LeaveCriticalSection( &(data->_mutex) );
2289   }
2290 }
2291 
getPortCount()2292 unsigned int MidiInWinMM :: getPortCount()
2293 {
2294   return midiInGetNumDevs();
2295 }
2296 
getPortName(unsigned int portNumber)2297 std::string MidiInWinMM :: getPortName( unsigned int portNumber )
2298 {
2299   std::string stringName;
2300   unsigned int nDevices = midiInGetNumDevs();
2301   if ( portNumber >= nDevices ) {
2302     std::ostringstream ost;
2303     ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2304     errorString_ = ost.str();
2305     error( RtMidiError::WARNING, errorString_ );
2306     return stringName;
2307   }
2308 
2309   MIDIINCAPS deviceCaps;
2310   midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
2311   stringName = ConvertToUTF8( deviceCaps.szPname );
2312 
2313   // Next lines added to add the portNumber to the name so that
2314   // the device's names are sure to be listed with individual names
2315   // even when they have the same brand name
2316 #ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
2317   std::ostringstream os;
2318   os << " ";
2319   os << portNumber;
2320   stringName += os.str();
2321 #endif
2322 
2323   return stringName;
2324 }
2325 
2326 //*********************************************************************//
2327 //  API: Windows MM
2328 //  Class Definitions: MidiOutWinMM
2329 //*********************************************************************//
2330 
MidiOutWinMM(const std::string & clientName)2331 MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi()
2332 {
2333   MidiOutWinMM::initialize( clientName );
2334 }
2335 
~MidiOutWinMM()2336 MidiOutWinMM :: ~MidiOutWinMM()
2337 {
2338   // Close a connection if it exists.
2339   MidiOutWinMM::closePort();
2340 
2341   // Cleanup.
2342   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2343   delete data;
2344 }
2345 
initialize(const std::string &)2346 void MidiOutWinMM :: initialize( const std::string& /*clientName*/ )
2347 {
2348   // We'll issue a warning here if no devices are available but not
2349   // throw an error since the user can plug something in later.
2350   unsigned int nDevices = midiOutGetNumDevs();
2351   if ( nDevices == 0 ) {
2352     errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available.";
2353     error( RtMidiError::WARNING, errorString_ );
2354   }
2355 
2356   // Save our api-specific connection information.
2357   WinMidiData *data = (WinMidiData *) new WinMidiData;
2358   apiData_ = (void *) data;
2359 }
2360 
getPortCount()2361 unsigned int MidiOutWinMM :: getPortCount()
2362 {
2363   return midiOutGetNumDevs();
2364 }
2365 
getPortName(unsigned int portNumber)2366 std::string MidiOutWinMM :: getPortName( unsigned int portNumber )
2367 {
2368   std::string stringName;
2369   unsigned int nDevices = midiOutGetNumDevs();
2370   if ( portNumber >= nDevices ) {
2371     std::ostringstream ost;
2372     ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2373     errorString_ = ost.str();
2374     error( RtMidiError::WARNING, errorString_ );
2375     return stringName;
2376   }
2377 
2378   MIDIOUTCAPS deviceCaps;
2379   midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));
2380   stringName = ConvertToUTF8( deviceCaps.szPname );
2381 
2382   // Next lines added to add the portNumber to the name so that
2383   // the device's names are sure to be listed with individual names
2384   // even when they have the same brand name
2385   std::ostringstream os;
2386 #ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
2387   os << " ";
2388   os << portNumber;
2389   stringName += os.str();
2390 #endif
2391 
2392   return stringName;
2393 }
2394 
openPort(unsigned int portNumber,const std::string &)2395 void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ )
2396 {
2397   if ( connected_ ) {
2398     errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!";
2399     error( RtMidiError::WARNING, errorString_ );
2400     return;
2401   }
2402 
2403   unsigned int nDevices = midiOutGetNumDevs();
2404   if (nDevices < 1) {
2405     errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!";
2406     error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
2407     return;
2408   }
2409 
2410   if ( portNumber >= nDevices ) {
2411     std::ostringstream ost;
2412     ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
2413     errorString_ = ost.str();
2414     error( RtMidiError::INVALID_PARAMETER, errorString_ );
2415     return;
2416   }
2417 
2418   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2419   MMRESULT result = midiOutOpen( &data->outHandle,
2420                                  portNumber,
2421                                  (DWORD)NULL,
2422                                  (DWORD)NULL,
2423                                  CALLBACK_NULL );
2424   if ( result != MMSYSERR_NOERROR ) {
2425     errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port.";
2426     error( RtMidiError::DRIVER_ERROR, errorString_ );
2427     return;
2428   }
2429 
2430   connected_ = true;
2431 }
2432 
closePort(void)2433 void MidiOutWinMM :: closePort( void )
2434 {
2435   if ( connected_ ) {
2436     WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2437     midiOutReset( data->outHandle );
2438     midiOutClose( data->outHandle );
2439     data->outHandle = 0;
2440     connected_ = false;
2441   }
2442 }
2443 
openVirtualPort(const std::string &)2444 void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ )
2445 {
2446   // This function cannot be implemented for the Windows MM MIDI API.
2447   errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
2448   error( RtMidiError::WARNING, errorString_ );
2449 }
2450 
sendMessage(const unsigned char * message,size_t size)2451 void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size )
2452 {
2453   if ( !connected_ ) return;
2454 
2455   unsigned int nBytes = static_cast<unsigned int>(size);
2456   if ( nBytes == 0 ) {
2457     errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!";
2458     error( RtMidiError::WARNING, errorString_ );
2459     return;
2460   }
2461 
2462   MMRESULT result;
2463   WinMidiData *data = static_cast<WinMidiData *> (apiData_);
2464   if ( message[0] == 0xF0 ) { // Sysex message
2465 
2466     // Allocate buffer for sysex data.
2467     char *buffer = (char *) malloc( nBytes );
2468     if ( buffer == NULL ) {
2469       errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!";
2470       error( RtMidiError::MEMORY_ERROR, errorString_ );
2471       return;
2472     }
2473 
2474     // Copy data to buffer.
2475     for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message[i];
2476 
2477     // Create and prepare MIDIHDR structure.
2478     MIDIHDR sysex;
2479     sysex.lpData = (LPSTR) buffer;
2480     sysex.dwBufferLength = nBytes;
2481     sysex.dwFlags = 0;
2482     result = midiOutPrepareHeader( data->outHandle,  &sysex, sizeof(MIDIHDR) );
2483     if ( result != MMSYSERR_NOERROR ) {
2484       free( buffer );
2485       errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header.";
2486       error( RtMidiError::DRIVER_ERROR, errorString_ );
2487       return;
2488     }
2489 
2490     // Send the message.
2491     result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) );
2492     if ( result != MMSYSERR_NOERROR ) {
2493       free( buffer );
2494       errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message.";
2495       error( RtMidiError::DRIVER_ERROR, errorString_ );
2496       return;
2497     }
2498 
2499     // Unprepare the buffer and MIDIHDR.
2500     while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 );
2501     free( buffer );
2502   }
2503   else { // Channel or system message.
2504 
2505     // Make sure the message size isn't too big.
2506     if ( nBytes > 3 ) {
2507       errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!";
2508       error( RtMidiError::WARNING, errorString_ );
2509       return;
2510     }
2511 
2512     // Pack MIDI bytes into double word.
2513     DWORD packet;
2514     unsigned char *ptr = (unsigned char *) &packet;
2515     for ( unsigned int i=0; i<nBytes; ++i ) {
2516       *ptr = message[i];
2517       ++ptr;
2518     }
2519 
2520     // Send the message immediately.
2521     result = midiOutShortMsg( data->outHandle, packet );
2522     if ( result != MMSYSERR_NOERROR ) {
2523       errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message.";
2524       error( RtMidiError::DRIVER_ERROR, errorString_ );
2525     }
2526   }
2527 }
2528 
2529 #endif  // __WINDOWS_MM__
2530 
2531 
2532 //*********************************************************************//
2533 //  API: UNIX JACK
2534 //
2535 //  Written primarily by Alexander Svetalkin, with updates for delta
2536 //  time by Gary Scavone, April 2011.
2537 //
2538 //  *********************************************************************//
2539 
2540 #if defined(__UNIX_JACK__)
2541 
2542 // JACK header files
2543 #include <jack/jack.h>
2544 #include <jack/midiport.h>
2545 #include <jack/ringbuffer.h>
2546 #ifdef HAVE_SEMAPHORE
2547   #include <semaphore.h>
2548 #endif
2549 
2550 #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
2551 
2552 struct JackMidiData {
2553   jack_client_t *client;
2554   jack_port_t *port;
2555   jack_ringbuffer_t *buffSize;
2556   jack_ringbuffer_t *buffMessage;
2557   jack_time_t lastTime;
2558 #ifdef HAVE_SEMAPHORE
2559   sem_t sem_cleanup;
2560   sem_t sem_needpost;
2561 #endif
2562   MidiInApi :: RtMidiInData *rtMidiIn;
2563   };
2564 
2565 //*********************************************************************//
2566 //  API: JACK
2567 //  Class Definitions: MidiInJack
2568 //*********************************************************************//
2569 
jackProcessIn(jack_nframes_t nframes,void * arg)2570 static int jackProcessIn( jack_nframes_t nframes, void *arg )
2571 {
2572   JackMidiData *jData = (JackMidiData *) arg;
2573   MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn;
2574   jack_midi_event_t event;
2575   jack_time_t time;
2576 
2577   // Is port created?
2578   if ( jData->port == NULL ) return 0;
2579   void *buff = jack_port_get_buffer( jData->port, nframes );
2580 
2581   // We have midi events in buffer
2582   int evCount = jack_midi_get_event_count( buff );
2583   for (int j = 0; j < evCount; j++) {
2584     MidiInApi::MidiMessage message;
2585     message.bytes.clear();
2586 
2587     jack_midi_event_get( &event, buff, j );
2588 
2589     for ( unsigned int i = 0; i < event.size; i++ )
2590       message.bytes.push_back( event.buffer[i] );
2591 
2592     // Compute the delta time.
2593     time = jack_get_time();
2594     if ( rtData->firstMessage == true )
2595       rtData->firstMessage = false;
2596     else
2597       message.timeStamp = ( time - jData->lastTime ) * 0.000001;
2598 
2599     jData->lastTime = time;
2600 
2601     if ( !rtData->continueSysex ) {
2602       if ( rtData->usingCallback ) {
2603         RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;
2604         callback( message.timeStamp, &message.bytes, rtData->userData );
2605       }
2606       else {
2607         // As long as we haven't reached our queue size limit, push the message.
2608         if (!rtData->queue.push(message))
2609           std::cerr << "\nMidiInJack: message queue limit reached!!\n\n";
2610       }
2611     }
2612   }
2613 
2614   return 0;
2615 }
2616 
MidiInJack(const std::string & clientName,unsigned int queueSizeLimit)2617 MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
2618 {
2619   MidiInJack::initialize( clientName );
2620 }
2621 
initialize(const std::string & clientName)2622 void MidiInJack :: initialize( const std::string& clientName )
2623 {
2624   JackMidiData *data = new JackMidiData;
2625   apiData_ = (void *) data;
2626 
2627   data->rtMidiIn = &inputData_;
2628   data->port = NULL;
2629   data->client = NULL;
2630   this->clientName = clientName;
2631 
2632   connect();
2633 }
2634 
connect()2635 void MidiInJack :: connect()
2636 {
2637   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2638   if ( data->client )
2639     return;
2640 
2641   // Initialize JACK client
2642   if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
2643     errorString_ = "MidiInJack::initialize: JACK server not running?";
2644     error( RtMidiError::WARNING, errorString_ );
2645     return;
2646   }
2647 
2648   jack_set_process_callback( data->client, jackProcessIn, data );
2649   jack_activate( data->client );
2650 }
2651 
~MidiInJack()2652 MidiInJack :: ~MidiInJack()
2653 {
2654   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2655   MidiInJack::closePort();
2656 
2657   if ( data->client )
2658     jack_client_close( data->client );
2659   delete data;
2660 }
2661 
openPort(unsigned int portNumber,const std::string & portName)2662 void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName )
2663 {
2664   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2665 
2666   connect();
2667 
2668   // Creating new port
2669   if ( data->port == NULL)
2670     data->port = jack_port_register( data->client, portName.c_str(),
2671                                      JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
2672 
2673   if ( data->port == NULL) {
2674     errorString_ = "MidiInJack::openPort: JACK error creating port";
2675     error( RtMidiError::DRIVER_ERROR, errorString_ );
2676     return;
2677   }
2678 
2679   // Connecting to the output
2680   std::string name = getPortName( portNumber );
2681   jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );
2682 }
2683 
openVirtualPort(const std::string & portName)2684 void MidiInJack :: openVirtualPort( const std::string &portName )
2685 {
2686   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2687 
2688   connect();
2689   if ( data->port == NULL )
2690     data->port = jack_port_register( data->client, portName.c_str(),
2691                                      JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
2692 
2693   if ( data->port == NULL ) {
2694     errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";
2695     error( RtMidiError::DRIVER_ERROR, errorString_ );
2696   }
2697 }
2698 
getPortCount()2699 unsigned int MidiInJack :: getPortCount()
2700 {
2701   int count = 0;
2702   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2703   connect();
2704   if ( !data->client )
2705     return 0;
2706 
2707   // List of available ports
2708   const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
2709 
2710   if ( ports == NULL ) return 0;
2711   while ( ports[count] != NULL )
2712     count++;
2713 
2714   free( ports );
2715 
2716   return count;
2717 }
2718 
getPortName(unsigned int portNumber)2719 std::string MidiInJack :: getPortName( unsigned int portNumber )
2720 {
2721   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2722   std::string retStr("");
2723 
2724   connect();
2725 
2726   // List of available ports
2727   const char **ports = jack_get_ports( data->client, NULL,
2728                                        JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
2729 
2730   // Check port validity
2731   if ( ports == NULL ) {
2732     errorString_ = "MidiInJack::getPortName: no ports available!";
2733     error( RtMidiError::WARNING, errorString_ );
2734     return retStr;
2735   }
2736 
2737   if ( ports[portNumber] == NULL ) {
2738     std::ostringstream ost;
2739     ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2740     errorString_ = ost.str();
2741     error( RtMidiError::WARNING, errorString_ );
2742   }
2743   else retStr.assign( ports[portNumber] );
2744 
2745   free( ports );
2746   return retStr;
2747 }
2748 
closePort()2749 void MidiInJack :: closePort()
2750 {
2751   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2752 
2753   if ( data->port == NULL ) return;
2754   jack_port_unregister( data->client, data->port );
2755   data->port = NULL;
2756 }
2757 
2758 //*********************************************************************//
2759 //  API: JACK
2760 //  Class Definitions: MidiOutJack
2761 //*********************************************************************//
2762 
2763 // Jack process callback
jackProcessOut(jack_nframes_t nframes,void * arg)2764 static int jackProcessOut( jack_nframes_t nframes, void *arg )
2765 {
2766   JackMidiData *data = (JackMidiData *) arg;
2767   jack_midi_data_t *midiData;
2768   int space;
2769 
2770   // Is port created?
2771   if ( data->port == NULL ) return 0;
2772 
2773   void *buff = jack_port_get_buffer( data->port, nframes );
2774   jack_midi_clear_buffer( buff );
2775 
2776   while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
2777     jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) );
2778     midiData = jack_midi_event_reserve( buff, 0, space );
2779 
2780     jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );
2781   }
2782 
2783 #ifdef HAVE_SEMAPHORE
2784   if (!sem_trywait(&data->sem_needpost))
2785     sem_post(&data->sem_cleanup);
2786 #endif
2787 
2788   return 0;
2789 }
2790 
MidiOutJack(const std::string & clientName)2791 MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi()
2792 {
2793   MidiOutJack::initialize( clientName );
2794 }
2795 
initialize(const std::string & clientName)2796 void MidiOutJack :: initialize( const std::string& clientName )
2797 {
2798   JackMidiData *data = new JackMidiData;
2799   apiData_ = (void *) data;
2800 
2801   data->port = NULL;
2802   data->client = NULL;
2803 #ifdef HAVE_SEMAPHORE
2804   sem_init(&data->sem_cleanup, 0, 0);
2805   sem_init(&data->sem_needpost, 0, 0);
2806 #endif
2807   this->clientName = clientName;
2808 
2809   connect();
2810 }
2811 
connect()2812 void MidiOutJack :: connect()
2813 {
2814   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2815   if ( data->client )
2816     return;
2817 
2818   // Initialize output ringbuffers
2819   data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
2820   data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
2821 
2822   // Initialize JACK client
2823   if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
2824     errorString_ = "MidiOutJack::initialize: JACK server not running?";
2825     error( RtMidiError::WARNING, errorString_ );
2826     return;
2827   }
2828 
2829   jack_set_process_callback( data->client, jackProcessOut, data );
2830   jack_activate( data->client );
2831 }
2832 
~MidiOutJack()2833 MidiOutJack :: ~MidiOutJack()
2834 {
2835   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2836   MidiOutJack::closePort();
2837 
2838   // Cleanup
2839   jack_ringbuffer_free( data->buffSize );
2840   jack_ringbuffer_free( data->buffMessage );
2841   if ( data->client ) {
2842     jack_client_close( data->client );
2843   }
2844 
2845 #ifdef HAVE_SEMAPHORE
2846   sem_destroy(&data->sem_cleanup);
2847   sem_destroy(&data->sem_needpost);
2848 #endif
2849 
2850   delete data;
2851 }
2852 
openPort(unsigned int portNumber,const std::string & portName)2853 void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName )
2854 {
2855   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2856 
2857   connect();
2858 
2859   // Creating new port
2860   if ( data->port == NULL )
2861     data->port = jack_port_register( data->client, portName.c_str(),
2862       JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
2863 
2864   if ( data->port == NULL ) {
2865     errorString_ = "MidiOutJack::openPort: JACK error creating port";
2866     error( RtMidiError::DRIVER_ERROR, errorString_ );
2867     return;
2868   }
2869 
2870   // Connecting to the output
2871   std::string name = getPortName( portNumber );
2872   jack_connect( data->client, jack_port_name( data->port ), name.c_str() );
2873 }
2874 
openVirtualPort(const std::string & portName)2875 void MidiOutJack :: openVirtualPort( const std::string &portName )
2876 {
2877   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2878 
2879   connect();
2880   if ( data->port == NULL )
2881     data->port = jack_port_register( data->client, portName.c_str(),
2882       JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
2883 
2884   if ( data->port == NULL ) {
2885     errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";
2886     error( RtMidiError::DRIVER_ERROR, errorString_ );
2887   }
2888 }
2889 
getPortCount()2890 unsigned int MidiOutJack :: getPortCount()
2891 {
2892   int count = 0;
2893   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2894   connect();
2895   if ( !data->client )
2896     return 0;
2897 
2898   // List of available ports
2899   const char **ports = jack_get_ports( data->client, NULL,
2900     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
2901 
2902   if ( ports == NULL ) return 0;
2903   while ( ports[count] != NULL )
2904     count++;
2905 
2906   free( ports );
2907 
2908   return count;
2909 }
2910 
getPortName(unsigned int portNumber)2911 std::string MidiOutJack :: getPortName( unsigned int portNumber )
2912 {
2913   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2914   std::string retStr("");
2915 
2916   connect();
2917 
2918   // List of available ports
2919   const char **ports = jack_get_ports( data->client, NULL,
2920     JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
2921 
2922   // Check port validity
2923   if ( ports == NULL) {
2924     errorString_ = "MidiOutJack::getPortName: no ports available!";
2925     error( RtMidiError::WARNING, errorString_ );
2926     return retStr;
2927   }
2928 
2929   if ( ports[portNumber] == NULL) {
2930     std::ostringstream ost;
2931     ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
2932     errorString_ = ost.str();
2933     error( RtMidiError::WARNING, errorString_ );
2934   }
2935   else retStr.assign( ports[portNumber] );
2936 
2937   free( ports );
2938   return retStr;
2939 }
2940 
closePort()2941 void MidiOutJack :: closePort()
2942 {
2943   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2944 
2945   if ( data->port == NULL ) return;
2946 
2947 #ifdef HAVE_SEMAPHORE
2948   struct timespec ts;
2949   if (clock_gettime(CLOCK_REALTIME, &ts) != -1)
2950   {
2951     ts.tv_sec += 1; // wait max one second
2952     sem_post(&data->sem_needpost);
2953     sem_timedwait(&data->sem_cleanup, &ts);
2954   }
2955 #endif
2956 
2957   jack_port_unregister( data->client, data->port );
2958   data->port = NULL;
2959 }
2960 
sendMessage(const unsigned char * message,size_t size)2961 void MidiOutJack :: sendMessage( const unsigned char *message, size_t size )
2962 {
2963   int nBytes = static_cast<int>(size);
2964   JackMidiData *data = static_cast<JackMidiData *> (apiData_);
2965 
2966   // Write full message to buffer
2967   jack_ringbuffer_write( data->buffMessage, ( const char * ) message,
2968                          nBytes );
2969   jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
2970 }
2971 
2972 #endif  // __UNIX_JACK__
2973