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