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 ); 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 ); 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 ); 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 ); 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 ); 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 ); 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 ); 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 ); 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: 239 MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } 240 RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } 241 void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} 242 void openVirtualPort( const std::string &/*portName*/ ) {} 243 void closePort( void ) {} 244 void setClientName( const std::string &/*clientName*/ ) {}; 245 void setPortName( const std::string &/*portName*/ ) {}; 246 unsigned int getPortCount( void ) { return 0; } 247 std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } 248 249 protected: 250 void initialize( const std::string& /*clientName*/ ) {} 251 }; 252 253 class MidiOutDummy: public MidiOutApi 254 { 255 public: 256 MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } 257 RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } 258 void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} 259 void openVirtualPort( const std::string &/*portName*/ ) {} 260 void closePort( void ) {} 261 void setClientName( const std::string &/*clientName*/ ) {}; 262 void setPortName( const std::string &/*portName*/ ) {}; 263 unsigned int getPortCount( void ) { return 0; } 264 std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } 265 void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {} 266 267 protected: 268 void initialize( const std::string& /*clientName*/ ) {} 269 }; 270 271 #endif 272 273 //*********************************************************************// 274 // RtMidi Definitions 275 //*********************************************************************// 276 277 RtMidi :: RtMidi() 278 : rtapi_(0) 279 { 280 } 281 282 RtMidi :: ~RtMidi() 283 { 284 delete rtapi_; 285 rtapi_ = 0; 286 } 287 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. 333 template<bool b> class StaticAssert { private: StaticAssert() {} }; 334 template<> class StaticAssert<true>{ public: StaticAssert() {} }; 335 class StaticAssertions { StaticAssertions() { 336 StaticAssert<rtmidi_num_api_names == RtMidi::NUM_APIS>(); 337 }}; 338 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 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 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 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 368 void RtMidi :: setClientName( const std::string &clientName ) 369 { 370 rtapi_->setClientName( clientName ); 371 } 372 373 void RtMidi :: setPortName( const std::string &portName ) 374 { 375 rtapi_->setPortName( portName ); 376 } 377 378 379 //*********************************************************************// 380 // RtMidiIn Definitions 381 //*********************************************************************// 382 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 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 442 RtMidiIn :: ~RtMidiIn() throw() 443 { 444 } 445 446 447 //*********************************************************************// 448 // RtMidiOut Definitions 449 //*********************************************************************// 450 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 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 509 RtMidiOut :: ~RtMidiOut() throw() 510 { 511 } 512 513 //*********************************************************************// 514 // Common MidiApi Definitions 515 //*********************************************************************// 516 517 MidiApi :: MidiApi( void ) 518 : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0) 519 { 520 } 521 522 MidiApi :: ~MidiApi( void ) 523 { 524 } 525 526 void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) 527 { 528 errorCallback_ = errorCallback; 529 errorCallbackUserData_ = userData; 530 } 531 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 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 574 MidiInApi :: ~MidiInApi( void ) 575 { 576 // Delete the MIDI queue. 577 if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; 578 } 579 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 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 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 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 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. 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 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 698 MidiOutApi :: MidiOutApi( void ) 699 : MidiApi() 700 { 701 } 702 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 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 889 MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) 890 : MidiInApi( queueSizeLimit ) 891 { 892 MidiInCore::initialize( clientName ); 893 } 894 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 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 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 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 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 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 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 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. 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. 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 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 1213 MidiOutCore :: MidiOutCore( const std::string &clientName ) 1214 : MidiOutApi() 1215 { 1216 MidiOutCore::initialize( clientName ); 1217 } 1218 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 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 1253 unsigned int MidiOutCore :: getPortCount() 1254 { 1255 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); 1256 return MIDIGetNumberOfDestinations(); 1257 } 1258 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 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 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 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 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 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 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 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 1716 MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) 1717 : MidiInApi( queueSizeLimit ) 1718 { 1719 MidiInAlsa::initialize( clientName ); 1720 } 1721 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 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. 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 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 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 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 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 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 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 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 2087 MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi() 2088 { 2089 MidiOutAlsa::initialize( clientName ); 2090 } 2091 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 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 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 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 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 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 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 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 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 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. 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 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 2503 MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) 2504 : MidiInApi( queueSizeLimit ) 2505 { 2506 MidiInWinMM::initialize( clientName ); 2507 } 2508 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 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 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 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 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 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 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 2669 unsigned int MidiInWinMM :: getPortCount() 2670 { 2671 return midiInGetNumDevs(); 2672 } 2673 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 2708 MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi() 2709 { 2710 MidiOutWinMM::initialize( clientName ); 2711 } 2712 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 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 2738 unsigned int MidiOutWinMM :: getPortCount() 2739 { 2740 return midiOutGetNumDevs(); 2741 } 2742 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 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 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 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 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 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 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 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 3045 MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) 3046 : MidiInApi( queueSizeLimit ) 3047 { 3048 MidiInJack::initialize( clientName ); 3049 } 3050 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 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 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 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 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 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 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 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 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 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 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 3244 MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi() 3245 { 3246 MidiOutJack::initialize( clientName ); 3247 } 3248 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 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 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 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 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 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 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 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 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 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 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