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