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