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