comparison CollidoscopeApp/src/RtMidi.cpp @ 0:02467299402e

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