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