/RtMidi.cpp
C++ | 3747 lines | 2794 code | 575 blank | 378 comment | 588 complexity | b595065c57d234c35379646fa077e891 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/**********************************************************************/ 2/*! \class RtMidi 3 \brief An abstract base class for realtime MIDI input/output. 4 5 This class implements some common functionality for the realtime 6 MIDI input/output subclasses RtMidiIn and RtMidiOut. 7 8 RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ 9 10 RtMidi: realtime MIDI i/o C++ classes 11 Copyright (c) 2003-2012 Gary P. Scavone 12 13 Permission is hereby granted, free of charge, to any person 14 obtaining a copy of this software and associated documentation files 15 (the "Software"), to deal in the Software without restriction, 16 including without limitation the rights to use, copy, modify, merge, 17 publish, distribute, sublicense, and/or sell copies of the Software, 18 and to permit persons to whom the Software is furnished to do so, 19 subject to the following conditions: 20 21 The above copyright notice and this permission notice shall be 22 included in all copies or substantial portions of the Software. 23 24 Any person wishing to distribute modifications to the Software is 25 asked to send the modifications to the original developer so that 26 they can be incorporated into the canonical version. This is, 27 however, not a binding provision of this license. 28 29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 32 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 33 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 34 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36*/ 37/**********************************************************************/ 38 39// RtMidi: Version 2.0.1 40 41#include "RtMidi.h" 42#include <sstream> 43 44//*********************************************************************// 45// RtMidi Definitions 46//*********************************************************************// 47 48void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw() 49{ 50 apis.clear(); 51 52 // The order here will control the order of RtMidi's API search in 53 // the constructor. 54#if defined(__MACOSX_CORE__) 55 apis.push_back( MACOSX_CORE ); 56#endif 57#if defined(__LINUX_ALSA__) 58 apis.push_back( LINUX_ALSA ); 59#endif 60#if defined(__UNIX_JACK__) 61 apis.push_back( UNIX_JACK ); 62#endif 63#if defined(__WINDOWS_MM__) 64 apis.push_back( WINDOWS_MM ); 65#endif 66#if defined(__WINDOWS_KS__) 67 apis.push_back( WINDOWS_KS ); 68#endif 69#if defined(__RTMIDI_DUMMY__) 70 apis.push_back( RTMIDI_DUMMY ); 71#endif 72} 73 74void RtMidi :: error( RtError::Type type, std::string errorString ) 75{ 76 if (type == RtError::WARNING) { 77 std::cerr << '\n' << errorString << "\n\n"; 78 } 79 else if (type == RtError::DEBUG_WARNING) { 80#if defined(__RTMIDI_DEBUG__) 81 std::cerr << '\n' << errorString << "\n\n"; 82#endif 83 } 84 else { 85 std::cerr << '\n' << errorString << "\n\n"; 86 throw RtError( errorString, type ); 87 } 88} 89 90//*********************************************************************// 91// RtMidiIn Definitions 92//*********************************************************************// 93 94void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) 95{ 96 if ( rtapi_ ) 97 delete rtapi_; 98 rtapi_ = 0; 99 100#if defined(__UNIX_JACK__) 101 if ( api == UNIX_JACK ) 102 rtapi_ = new MidiInJack( clientName, queueSizeLimit ); 103#endif 104#if defined(__LINUX_ALSA__) 105 if ( api == LINUX_ALSA ) 106 rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); 107#endif 108#if defined(__WINDOWS_MM__) 109 if ( api == WINDOWS_MM ) 110 rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); 111#endif 112#if defined(__WINDOWS_KS__) 113 if ( api == WINDOWS_KS ) 114 rtapi_ = new MidiInWinKS( clientName, queueSizeLimit ); 115#endif 116#if defined(__MACOSX_CORE__) 117 if ( api == MACOSX_CORE ) 118 rtapi_ = new MidiInCore( clientName, queueSizeLimit ); 119#endif 120#if defined(__RTMIDI_DUMMY__) 121 if ( api == RTMIDI_DUMMY ) 122 rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); 123#endif 124} 125 126RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) 127{ 128 rtapi_ = 0; 129 130 if ( api != UNSPECIFIED ) { 131 // Attempt to open the specified API. 132 openMidiApi( api, clientName, queueSizeLimit ); 133 if ( rtapi_ ) return; 134 135 // No compiled support for specified API value. Issue a debug 136 // warning and continue as if no API was specified. 137 RtMidi::error( RtError::WARNING, "RtMidiIn: no compiled support for specified API argument!" ); 138 } 139 140 // Iterate through the compiled APIs and return as soon as we find 141 // one with at least one port or we reach the end of the list. 142 std::vector< RtMidi::Api > apis; 143 getCompiledApi( apis ); 144 for ( unsigned int i=0; i<apis.size(); i++ ) { 145 openMidiApi( apis[i], clientName, queueSizeLimit ); 146 if ( rtapi_->getPortCount() ) break; 147 } 148 149 if ( rtapi_ ) return; 150 151 // It should not be possible to get here because the preprocessor 152 // definition __RTMIDI_DUMMY__ is automatically defined if no 153 // API-specific definitions are passed to the compiler. But just in 154 // case something weird happens, we'll print out an error message. 155 RtMidi::error( RtError::WARNING, "RtMidiIn: no compiled API support found ... critical error!!" ); 156} 157 158RtMidiIn :: ~RtMidiIn() throw() 159{ 160 delete rtapi_; 161} 162 163 164//*********************************************************************// 165// RtMidiOut Definitions 166//*********************************************************************// 167 168void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName ) 169{ 170 if ( rtapi_ ) 171 delete rtapi_; 172 rtapi_ = 0; 173 174#if defined(__UNIX_JACK__) 175 if ( api == UNIX_JACK ) 176 rtapi_ = new MidiOutJack( clientName ); 177#endif 178#if defined(__LINUX_ALSA__) 179 if ( api == LINUX_ALSA ) 180 rtapi_ = new MidiOutAlsa( clientName ); 181#endif 182#if defined(__WINDOWS_MM__) 183 if ( api == WINDOWS_MM ) 184 rtapi_ = new MidiOutWinMM( clientName ); 185#endif 186#if defined(__WINDOWS_KS__) 187 if ( api == WINDOWS_KS ) 188 rtapi_ = new MidiOutWinKS( clientName ); 189#endif 190#if defined(__MACOSX_CORE__) 191 if ( api == MACOSX_CORE ) 192 rtapi_ = new MidiOutCore( clientName ); 193#endif 194#if defined(__RTMIDI_DUMMY__) 195 if ( api == RTMIDI_DUMMY ) 196 rtapi_ = new MidiOutDummy( clientName ); 197#endif 198} 199 200RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName ) 201{ 202 rtapi_ = 0; 203 204 if ( api != UNSPECIFIED ) { 205 // Attempt to open the specified API. 206 openMidiApi( api, clientName ); 207 if ( rtapi_ ) return; 208 209 // No compiled support for specified API value. Issue a debug 210 // warning and continue as if no API was specified. 211 RtMidi::error( RtError::WARNING, "RtMidiOut: no compiled support for specified API argument!" ); 212 } 213 214 // Iterate through the compiled APIs and return as soon as we find 215 // one with at least one port or we reach the end of the list. 216 std::vector< RtMidi::Api > apis; 217 getCompiledApi( apis ); 218 for ( unsigned int i=0; i<apis.size(); i++ ) { 219 openMidiApi( apis[i], clientName ); 220 if ( rtapi_->getPortCount() ) break; 221 } 222 223 if ( rtapi_ ) return; 224 225 // It should not be possible to get here because the preprocessor 226 // definition __RTMIDI_DUMMY__ is automatically defined if no 227 // API-specific definitions are passed to the compiler. But just in 228 // case something weird happens, we'll print out an error message. 229 RtMidi::error( RtError::WARNING, "RtMidiOut: no compiled API support found ... critical error!!" ); 230} 231 232RtMidiOut :: ~RtMidiOut() throw() 233{ 234 delete rtapi_; 235} 236 237//*********************************************************************// 238// Common MidiInApi Definitions 239//*********************************************************************// 240 241MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) 242 : apiData_( 0 ), connected_( false ) 243{ 244 // Allocate the MIDI queue. 245 inputData_.queue.ringSize = queueSizeLimit; 246 if ( inputData_.queue.ringSize > 0 ) 247 inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; 248} 249 250MidiInApi :: ~MidiInApi( void ) 251{ 252 // Delete the MIDI queue. 253 if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; 254} 255 256void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) 257{ 258 if ( inputData_.usingCallback ) { 259 errorString_ = "MidiInApi::setCallback: a callback function is already set!"; 260 RtMidi::error( RtError::WARNING, errorString_ ); 261 return; 262 } 263 264 if ( !callback ) { 265 errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; 266 RtMidi::error( RtError::WARNING, errorString_ ); 267 return; 268 } 269 270 inputData_.userCallback = (void *) callback; 271 inputData_.userData = userData; 272 inputData_.usingCallback = true; 273} 274 275void MidiInApi :: cancelCallback() 276{ 277 if ( !inputData_.usingCallback ) { 278 errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; 279 RtMidi::error( RtError::WARNING, errorString_ ); 280 return; 281 } 282 283 inputData_.userCallback = 0; 284 inputData_.userData = 0; 285 inputData_.usingCallback = false; 286} 287 288void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) 289{ 290 inputData_.ignoreFlags = 0; 291 if ( midiSysex ) inputData_.ignoreFlags = 0x01; 292 if ( midiTime ) inputData_.ignoreFlags |= 0x02; 293 if ( midiSense ) inputData_.ignoreFlags |= 0x04; 294} 295 296double MidiInApi :: getMessage( std::vector<unsigned char> *message ) 297{ 298 message->clear(); 299 300 if ( inputData_.usingCallback ) { 301 errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; 302 RtMidi::error( RtError::WARNING, errorString_ ); 303 return 0.0; 304 } 305 306 if ( inputData_.queue.size == 0 ) return 0.0; 307 308 // Copy queued message to the vector pointer argument and then "pop" it. 309 std::vector<unsigned char> *bytes = &(inputData_.queue.ring[inputData_.queue.front].bytes); 310 message->assign( bytes->begin(), bytes->end() ); 311 double deltaTime = inputData_.queue.ring[inputData_.queue.front].timeStamp; 312 inputData_.queue.size--; 313 inputData_.queue.front++; 314 if ( inputData_.queue.front == inputData_.queue.ringSize ) 315 inputData_.queue.front = 0; 316 317 return deltaTime; 318} 319 320//*********************************************************************// 321// Common MidiOutApi Definitions 322//*********************************************************************// 323 324MidiOutApi :: MidiOutApi( void ) 325 : apiData_( 0 ), connected_( false ) 326{ 327} 328 329MidiOutApi :: ~MidiOutApi( void ) 330{ 331} 332 333// *************************************************** // 334// 335// OS/API-specific methods. 336// 337// *************************************************** // 338 339#if defined(__MACOSX_CORE__) 340 341// The CoreMIDI API is based on the use of a callback function for 342// MIDI input. We convert the system specific time stamps to delta 343// time values. 344 345// OS-X CoreMIDI header files. 346#include <CoreMIDI/CoreMIDI.h> 347#include <CoreAudio/HostTime.h> 348#include <CoreServices/CoreServices.h> 349 350// A structure to hold variables related to the CoreMIDI API 351// implementation. 352struct CoreMidiData { 353 MIDIClientRef client; 354 MIDIPortRef port; 355 MIDIEndpointRef endpoint; 356 MIDIEndpointRef destinationId; 357 unsigned long long lastTime; 358 MIDISysexSendRequest sysexreq; 359}; 360 361//*********************************************************************// 362// API: OS-X 363// Class Definitions: MidiInCore 364//*********************************************************************// 365 366void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) 367{ 368 MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef); 369 CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData); 370 371 unsigned char status; 372 unsigned short nBytes, iByte, size; 373 unsigned long long time; 374 375 bool& continueSysex = data->continueSysex; 376 MidiInApi::MidiMessage& message = data->message; 377 378 const MIDIPacket *packet = &list->packet[0]; 379 for ( unsigned int i=0; i<list->numPackets; ++i ) { 380 381 // My interpretation of the CoreMIDI documentation: all message 382 // types, except sysex, are complete within a packet and there may 383 // be several of them in a single packet. Sysex messages can be 384 // broken across multiple packets and PacketLists but are bundled 385 // alone within each packet (these packets do not contain other 386 // message types). If sysex messages are split across multiple 387 // MIDIPacketLists, they must be handled by multiple calls to this 388 // function. 389 390 nBytes = packet->length; 391 if ( nBytes == 0 ) continue; 392 393 // Calculate time stamp. 394 395 if ( data->firstMessage ) { 396 message.timeStamp = 0.0; 397 data->firstMessage = false; 398 } 399 else { 400 time = packet->timeStamp; 401 if ( time == 0 ) { // this happens when receiving asynchronous sysex messages 402 time = AudioGetCurrentHostTime(); 403 } 404 time -= apiData->lastTime; 405 time = AudioConvertHostTimeToNanos( time ); 406 if ( !continueSysex ) 407 message.timeStamp = time * 0.000000001; 408 } 409 apiData->lastTime = packet->timeStamp; 410 if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages 411 apiData->lastTime = AudioGetCurrentHostTime(); 412 } 413 //std::cout << "TimeStamp = " << packet->timeStamp << std::endl; 414 415 iByte = 0; 416 if ( continueSysex ) { 417 // We have a continuing, segmented sysex message. 418 if ( !( data->ignoreFlags & 0x01 ) ) { 419 // If we're not ignoring sysex messages, copy the entire packet. 420 for ( unsigned int j=0; j<nBytes; ++j ) 421 message.bytes.push_back( packet->data[j] ); 422 } 423 continueSysex = packet->data[nBytes-1] != 0xF7; 424 425 if ( !continueSysex ) { 426 // If not a continuing sysex message, invoke the user callback function or queue the message. 427 if ( data->usingCallback ) { 428 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 429 callback( message.timeStamp, &message.bytes, data->userData ); 430 } 431 else { 432 // As long as we haven't reached our queue size limit, push the message. 433 if ( data->queue.size < data->queue.ringSize ) { 434 data->queue.ring[data->queue.back++] = message; 435 if ( data->queue.back == data->queue.ringSize ) 436 data->queue.back = 0; 437 data->queue.size++; 438 } 439 else 440 std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; 441 } 442 message.bytes.clear(); 443 } 444 } 445 else { 446 while ( iByte < nBytes ) { 447 size = 0; 448 // We are expecting that the next byte in the packet is a status byte. 449 status = packet->data[iByte]; 450 if ( !(status & 0x80) ) break; 451 // Determine the number of bytes in the MIDI message. 452 if ( status < 0xC0 ) size = 3; 453 else if ( status < 0xE0 ) size = 2; 454 else if ( status < 0xF0 ) size = 3; 455 else if ( status == 0xF0 ) { 456 // A MIDI sysex 457 if ( data->ignoreFlags & 0x01 ) { 458 size = 0; 459 iByte = nBytes; 460 } 461 else size = nBytes - iByte; 462 continueSysex = packet->data[nBytes-1] != 0xF7; 463 } 464 else if ( status == 0xF1 ) { 465 // A MIDI time code message 466 if ( data->ignoreFlags & 0x02 ) { 467 size = 0; 468 iByte += 2; 469 } 470 else size = 2; 471 } 472 else if ( status == 0xF2 ) size = 3; 473 else if ( status == 0xF3 ) size = 2; 474 else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { 475 // A MIDI timing tick message and we're ignoring it. 476 size = 0; 477 iByte += 1; 478 } 479 else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { 480 // A MIDI active sensing message and we're ignoring it. 481 size = 0; 482 iByte += 1; 483 } 484 else size = 1; 485 486 // Copy the MIDI data to our vector. 487 if ( size ) { 488 message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); 489 if ( !continueSysex ) { 490 // If not a continuing sysex message, invoke the user callback function or queue the message. 491 if ( data->usingCallback ) { 492 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 493 callback( message.timeStamp, &message.bytes, data->userData ); 494 } 495 else { 496 // As long as we haven't reached our queue size limit, push the message. 497 if ( data->queue.size < data->queue.ringSize ) { 498 data->queue.ring[data->queue.back++] = message; 499 if ( data->queue.back == data->queue.ringSize ) 500 data->queue.back = 0; 501 data->queue.size++; 502 } 503 else 504 std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; 505 } 506 message.bytes.clear(); 507 } 508 iByte += size; 509 } 510 } 511 } 512 packet = MIDIPacketNext(packet); 513 } 514} 515 516MidiInCore :: MidiInCore( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) 517{ 518 initialize( clientName ); 519} 520 521MidiInCore :: ~MidiInCore( void ) 522{ 523 // Close a connection if it exists. 524 closePort(); 525 526 // Cleanup. 527 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 528 MIDIClientDispose( data->client ); 529 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); 530 delete data; 531} 532 533void MidiInCore :: initialize( const std::string& clientName ) 534{ 535 // Set up our client. 536 MIDIClientRef client; 537 OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); 538 if ( result != noErr ) { 539 errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object."; 540 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 541 } 542 543 // Save our api-specific connection information. 544 CoreMidiData *data = (CoreMidiData *) new CoreMidiData; 545 data->client = client; 546 data->endpoint = 0; 547 apiData_ = (void *) data; 548 inputData_.apiData = (void *) data; 549} 550 551void MidiInCore :: openPort( unsigned int portNumber, const std::string portName ) 552{ 553 if ( connected_ ) { 554 errorString_ = "MidiInCore::openPort: a valid connection already exists!"; 555 RtMidi::error( RtError::WARNING, errorString_ ); 556 return; 557 } 558 559 unsigned int nSrc = MIDIGetNumberOfSources(); 560 if (nSrc < 1) { 561 errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; 562 RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 563 } 564 565 std::ostringstream ost; 566 if ( portNumber >= nSrc ) { 567 ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 568 errorString_ = ost.str(); 569 RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 570 } 571 572 MIDIPortRef port; 573 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 574 OSStatus result = MIDIInputPortCreate( data->client, 575 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 576 midiInputCallback, (void *)&inputData_, &port ); 577 if ( result != noErr ) { 578 MIDIClientDispose( data->client ); 579 errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; 580 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 581 } 582 583 // Get the desired input source identifier. 584 MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); 585 if ( endpoint == 0 ) { 586 MIDIPortDispose( port ); 587 MIDIClientDispose( data->client ); 588 errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; 589 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 590 } 591 592 // Make the connection. 593 result = MIDIPortConnectSource( port, endpoint, NULL ); 594 if ( result != noErr ) { 595 MIDIPortDispose( port ); 596 MIDIClientDispose( data->client ); 597 errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; 598 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 599 } 600 601 // Save our api-specific port information. 602 data->port = port; 603 604 connected_ = true; 605} 606 607void MidiInCore :: openVirtualPort( const std::string portName ) 608{ 609 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 610 611 // Create a virtual MIDI input destination. 612 MIDIEndpointRef endpoint; 613 OSStatus result = MIDIDestinationCreate( data->client, 614 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 615 midiInputCallback, (void *)&inputData_, &endpoint ); 616 if ( result != noErr ) { 617 errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; 618 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 619 } 620 621 // Save our api-specific connection information. 622 data->endpoint = endpoint; 623} 624 625void MidiInCore :: closePort( void ) 626{ 627 if ( connected_ ) { 628 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 629 MIDIPortDispose( data->port ); 630 connected_ = false; 631 } 632} 633 634unsigned int MidiInCore :: getPortCount() 635{ 636 return MIDIGetNumberOfSources(); 637} 638 639// This function was submitted by Douglas Casey Tucker and apparently 640// derived largely from PortMidi. 641CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) 642{ 643 CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); 644 CFStringRef str; 645 646 // Begin with the endpoint's name. 647 str = NULL; 648 MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); 649 if ( str != NULL ) { 650 CFStringAppend( result, str ); 651 CFRelease( str ); 652 } 653 654 MIDIEntityRef entity = NULL; 655 MIDIEndpointGetEntity( endpoint, &entity ); 656 if ( entity == 0 ) 657 // probably virtual 658 return result; 659 660 if ( CFStringGetLength( result ) == 0 ) { 661 // endpoint name has zero length -- try the entity 662 str = NULL; 663 MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); 664 if ( str != NULL ) { 665 CFStringAppend( result, str ); 666 CFRelease( str ); 667 } 668 } 669 // now consider the device's name 670 MIDIDeviceRef device = 0; 671 MIDIEntityGetDevice( entity, &device ); 672 if ( device == 0 ) 673 return result; 674 675 str = NULL; 676 MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); 677 if ( CFStringGetLength( result ) == 0 ) { 678 CFRelease( result ); 679 return str; 680 } 681 if ( str != NULL ) { 682 // if an external device has only one entity, throw away 683 // the endpoint name and just use the device name 684 if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { 685 CFRelease( result ); 686 return str; 687 } else { 688 if ( CFStringGetLength( str ) == 0 ) { 689 CFRelease( str ); 690 return result; 691 } 692 // does the entity name already start with the device name? 693 // (some drivers do this though they shouldn't) 694 // if so, do not prepend 695 if ( CFStringCompareWithOptions( result, /* endpoint name */ 696 str /* device name */, 697 CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { 698 // prepend the device name to the entity name 699 if ( CFStringGetLength( result ) > 0 ) 700 CFStringInsert( result, 0, CFSTR(" ") ); 701 CFStringInsert( result, 0, str ); 702 } 703 CFRelease( str ); 704 } 705 } 706 return result; 707} 708 709// This function was submitted by Douglas Casey Tucker and apparently 710// derived largely from PortMidi. 711static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) 712{ 713 CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); 714 CFStringRef str; 715 OSStatus err; 716 int i; 717 718 // Does the endpoint have connections? 719 CFDataRef connections = NULL; 720 int nConnected = 0; 721 bool anyStrings = false; 722 err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); 723 if ( connections != NULL ) { 724 // It has connections, follow them 725 // Concatenate the names of all connected devices 726 nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); 727 if ( nConnected ) { 728 const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); 729 for ( i=0; i<nConnected; ++i, ++pid ) { 730 MIDIUniqueID id = EndianS32_BtoN( *pid ); 731 MIDIObjectRef connObject; 732 MIDIObjectType connObjectType; 733 err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType ); 734 if ( err == noErr ) { 735 if ( connObjectType == kMIDIObjectType_ExternalSource || 736 connObjectType == kMIDIObjectType_ExternalDestination ) { 737 // Connected to an external device's endpoint (10.3 and later). 738 str = EndpointName( (MIDIEndpointRef)(connObject), true ); 739 } else { 740 // Connected to an external device (10.2) (or something else, catch- 741 str = NULL; 742 MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str ); 743 } 744 if ( str != NULL ) { 745 if ( anyStrings ) 746 CFStringAppend( result, CFSTR(", ") ); 747 else anyStrings = true; 748 CFStringAppend( result, str ); 749 CFRelease( str ); 750 } 751 } 752 } 753 } 754 CFRelease( connections ); 755 } 756 if ( anyStrings ) 757 return result; 758 759 // Here, either the endpoint had no connections, or we failed to obtain names 760 return EndpointName( endpoint, false ); 761} 762 763std::string MidiInCore :: getPortName( unsigned int portNumber ) 764{ 765 CFStringRef nameRef; 766 MIDIEndpointRef portRef; 767 std::ostringstream ost; 768 char name[128]; 769 770 std::string stringName; 771 if ( portNumber >= MIDIGetNumberOfSources() ) { 772 ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 773 errorString_ = ost.str(); 774 RtMidi::error( RtError::WARNING, errorString_ ); 775 //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 776 return stringName; 777 } 778 779 portRef = MIDIGetSource( portNumber ); 780 nameRef = ConnectedEndpointName(portRef); 781 CFStringGetCString( nameRef, name, sizeof(name), 0); 782 CFRelease( nameRef ); 783 784 return stringName = name; 785} 786 787//*********************************************************************// 788// API: OS-X 789// Class Definitions: MidiOutCore 790//*********************************************************************// 791 792MidiOutCore :: MidiOutCore( const std::string clientName ) : MidiOutApi() 793{ 794 initialize( clientName ); 795} 796 797MidiOutCore :: ~MidiOutCore( void ) 798{ 799 // Close a connection if it exists. 800 closePort(); 801 802 // Cleanup. 803 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 804 MIDIClientDispose( data->client ); 805 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); 806 delete data; 807} 808 809void MidiOutCore :: initialize( const std::string& clientName ) 810{ 811 // Set up our client. 812 MIDIClientRef client; 813 OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); 814 if ( result != noErr ) { 815 errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object."; 816 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 817 } 818 819 // Save our api-specific connection information. 820 CoreMidiData *data = (CoreMidiData *) new CoreMidiData; 821 data->client = client; 822 data->endpoint = 0; 823 apiData_ = (void *) data; 824} 825 826unsigned int MidiOutCore :: getPortCount() 827{ 828 return MIDIGetNumberOfDestinations(); 829} 830 831std::string MidiOutCore :: getPortName( unsigned int portNumber ) 832{ 833 CFStringRef nameRef; 834 MIDIEndpointRef portRef; 835 std::ostringstream ost; 836 char name[128]; 837 838 std::string stringName; 839 if ( portNumber >= MIDIGetNumberOfDestinations() ) { 840 ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 841 errorString_ = ost.str(); 842 RtMidi::error( RtError::WARNING, errorString_ ); 843 return stringName; 844 //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 845 } 846 847 portRef = MIDIGetDestination( portNumber ); 848 nameRef = ConnectedEndpointName(portRef); 849 CFStringGetCString( nameRef, name, sizeof(name), 0); 850 CFRelease( nameRef ); 851 852 return stringName = name; 853} 854 855void MidiOutCore :: openPort( unsigned int portNumber, const std::string portName ) 856{ 857 if ( connected_ ) { 858 errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; 859 RtMidi::error( RtError::WARNING, errorString_ ); 860 return; 861 } 862 863 unsigned int nDest = MIDIGetNumberOfDestinations(); 864 if (nDest < 1) { 865 errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; 866 RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 867 } 868 869 std::ostringstream ost; 870 if ( portNumber >= nDest ) { 871 ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 872 errorString_ = ost.str(); 873 RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 874 } 875 876 MIDIPortRef port; 877 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 878 OSStatus result = MIDIOutputPortCreate( data->client, 879 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 880 &port ); 881 if ( result != noErr ) { 882 MIDIClientDispose( data->client ); 883 errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; 884 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 885 } 886 887 // Get the desired output port identifier. 888 MIDIEndpointRef destination = MIDIGetDestination( portNumber ); 889 if ( destination == 0 ) { 890 MIDIPortDispose( port ); 891 MIDIClientDispose( data->client ); 892 errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; 893 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 894 } 895 896 // Save our api-specific connection information. 897 data->port = port; 898 data->destinationId = destination; 899 connected_ = true; 900} 901 902void MidiOutCore :: closePort( void ) 903{ 904 if ( connected_ ) { 905 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 906 MIDIPortDispose( data->port ); 907 connected_ = false; 908 } 909} 910 911void MidiOutCore :: openVirtualPort( std::string portName ) 912{ 913 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 914 915 if ( data->endpoint ) { 916 errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; 917 RtMidi::error( RtError::WARNING, errorString_ ); 918 return; 919 } 920 921 // Create a virtual MIDI output source. 922 MIDIEndpointRef endpoint; 923 OSStatus result = MIDISourceCreate( data->client, 924 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 925 &endpoint ); 926 if ( result != noErr ) { 927 errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; 928 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 929 } 930 931 // Save our api-specific connection information. 932 data->endpoint = endpoint; 933} 934 935char *sysexBuffer = 0; 936 937void sysexCompletionProc( MIDISysexSendRequest * sreq ) 938{ 939 //std::cout << "Completed SysEx send\n"; 940 delete sysexBuffer; 941 sysexBuffer = 0; 942} 943 944void MidiOutCore :: sendMessage( std::vector<unsigned char> *message ) 945{ 946 // We use the MIDISendSysex() function to asynchronously send sysex 947 // messages. Otherwise, we use a single CoreMidi MIDIPacket. 948 unsigned int nBytes = message->size(); 949 if ( nBytes == 0 ) { 950 errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; 951 RtMidi::error( RtError::WARNING, errorString_ ); 952 return; 953 } 954 955 // unsigned int packetBytes, bytesLeft = nBytes; 956 // unsigned int messageIndex = 0; 957 MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); 958 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 959 OSStatus result; 960 961 if ( message->at(0) == 0xF0 ) { 962 963 while ( sysexBuffer != 0 ) usleep( 1000 ); // sleep 1 ms 964 965 sysexBuffer = new char[nBytes]; 966 if ( sysexBuffer == NULL ) { 967 errorString_ = "MidiOutCore::sendMessage: error allocating sysex message memory!"; 968 RtMidi::error( RtError::MEMORY_ERROR, errorString_ ); 969 } 970 971 // Copy data to buffer. 972 for ( unsigned int i=0; i<nBytes; ++i ) sysexBuffer[i] = message->at(i); 973 974 data->sysexreq.destination = data->destinationId; 975 data->sysexreq.data = (Byte *)sysexBuffer; 976 data->sysexreq.bytesToSend = nBytes; 977 data->sysexreq.complete = 0; 978 data->sysexreq.completionProc = sysexCompletionProc; 979 data->sysexreq.completionRefCon = &(data->sysexreq); 980 981 result = MIDISendSysex( &(data->sysexreq) ); 982 if ( result != noErr ) { 983 errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; 984 RtMidi::error( RtError::WARNING, errorString_ ); 985 } 986 return; 987 } 988 else if ( nBytes > 3 ) { 989 errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; 990 RtMidi::error( RtError::WARNING, errorString_ ); 991 return; 992 } 993 994 MIDIPacketList packetList; 995 MIDIPacket *packet = MIDIPacketListInit( &packetList ); 996 packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) ); 997 if ( !packet ) { 998 errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; 999 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1000 } 1001 1002 // Send to any destinations that may have connected to us. 1003 if ( data->endpoint ) { 1004 result = MIDIReceived( data->endpoint, &packetList ); 1005 if ( result != noErr ) { 1006 errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; 1007 RtMidi::error( RtError::WARNING, errorString_ ); 1008 } 1009 } 1010 1011 // And send to an explicit destination port if we're connected. 1012 if ( connected_ ) { 1013 result = MIDISend( data->port, data->destinationId, &packetList ); 1014 if ( result != noErr ) { 1015 errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; 1016 RtMidi::error( RtError::WARNING, errorString_ ); 1017 } 1018 } 1019 1020} 1021 1022#endif // __MACOSX_CORE__ 1023 1024 1025//*********************************************************************// 1026// API: LINUX ALSA SEQUENCER 1027//*********************************************************************// 1028 1029// API information found at: 1030// - http://www.alsa-project.org/documentation.php#Library 1031 1032#if defined(__LINUX_ALSA__) 1033 1034// The ALSA Sequencer API is based on the use of a callback function for 1035// MIDI input. 1036// 1037// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer 1038// time stamps and other assorted fixes!!! 1039 1040// If you don't need timestamping for incoming MIDI events, define the 1041// preprocessor definition AVOID_TIMESTAMPING to save resources 1042// associated with the ALSA sequencer queues. 1043 1044#include <pthread.h> 1045#include <sys/time.h> 1046 1047// ALSA header file. 1048#include <alsa/asoundlib.h> 1049 1050// Global sequencer instance created when first In/Out object is 1051// created, then destroyed when last In/Out is deleted. 1052static snd_seq_t *s_seq = NULL; 1053 1054// Variable to keep track of how many ports are open. 1055static unsigned int s_numPorts = 0; 1056 1057// The client name to use when creating the sequencer, which is 1058// currently set on the first call to createSequencer. 1059static std::string s_clientName = "RtMidi Client"; 1060 1061// A structure to hold variables related to the ALSA API 1062// implementation. 1063struct AlsaMidiData { 1064 snd_seq_t *seq; 1065 unsigned int portNum; 1066 int vport; 1067 snd_seq_port_subscribe_t *subscription; 1068 snd_midi_event_t *coder; 1069 unsigned int bufferSize; 1070 unsigned char *buffer; 1071 pthread_t thread; 1072 pthread_t dummy_thread_id; 1073 unsigned long long lastTime; 1074 int queue_id; // an input queue is needed to get timestamped events 1075 int trigger_fds[2]; 1076}; 1077 1078#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) 1079 1080snd_seq_t* createSequencer( const std::string& clientName ) 1081{ 1082 // Set up the ALSA sequencer client. 1083 if ( s_seq == NULL ) { 1084 int result = snd_seq_open(&s_seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); 1085 if ( result < 0 ) { 1086 s_seq = NULL; 1087 } 1088 else { 1089 // Set client name, use current name if given string is empty. 1090 if ( clientName != "" ) { 1091 s_clientName = clientName; 1092 } 1093 snd_seq_set_client_name( s_seq, s_clientName.c_str() ); 1094 } 1095 } 1096 1097 // Increment port count. 1098 s_numPorts++; 1099 1100 return s_seq; 1101} 1102 1103void freeSequencer ( void ) 1104{ 1105 s_numPorts--; 1106 if ( s_numPorts == 0 && s_seq != NULL ) { 1107 snd_seq_close( s_seq ); 1108 s_seq = NULL; 1109 } 1110} 1111 1112//*********************************************************************// 1113// API: LINUX ALSA 1114// Class Definitions: MidiInAlsa 1115//*********************************************************************// 1116 1117extern "C" void *alsaMidiHandler( void *ptr ) 1118{ 1119 MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr); 1120 AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData); 1121 1122 long nBytes; 1123 unsigned long long time, lastTime; 1124 bool continueSysex = false; 1125 bool doDecode = false; 1126 MidiInApi::MidiMessage message; 1127 int poll_fd_count; 1128 struct pollfd *poll_fds; 1129 1130 snd_seq_event_t *ev; 1131 int result; 1132 apiData->bufferSize = 32; 1133 result = snd_midi_event_new( 0, &apiData->coder ); 1134 if ( result < 0 ) { 1135 data->doInput = false; 1136 std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; 1137 return 0; 1138 } 1139 unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); 1140 if ( buffer == NULL ) { 1141 data->doInput = false; 1142 snd_midi_event_free( apiData->coder ); 1143 apiData->coder = 0; 1144 std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; 1145 return 0; 1146 } 1147 snd_midi_event_init( apiData->coder ); 1148 snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages 1149 1150 poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; 1151 poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); 1152 snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); 1153 poll_fds[0].fd = apiData->trigger_fds[0]; 1154 poll_fds[0].events = POLLIN; 1155 1156 while ( data->doInput ) { 1157 1158 if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { 1159 // No data pending 1160 if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { 1161 if ( poll_fds[0].revents & POLLIN ) { 1162 bool dummy; 1163 int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); 1164 (void) res; 1165 } 1166 } 1167 continue; 1168 } 1169 1170 // If here, there should be data. 1171 result = snd_seq_event_input( apiData->seq, &ev ); 1172 if ( result == -ENOSPC ) { 1173 std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; 1174 continue; 1175 } 1176 else if ( result <= 0 ) { 1177 std::cerr << "MidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; 1178 continue; 1179 } 1180 1181 // This is a bit weird, but we now have to decode an ALSA MIDI 1182 // event (back) into MIDI bytes. We'll ignore non-MIDI types. 1183 if ( !continueSysex ) message.bytes.clear(); 1184 1185 doDecode = false; 1186 switch ( ev->type ) { 1187 1188 case SND_SEQ_EVENT_PORT_SUBSCRIBED: 1189#if defined(__RTMIDI_DEBUG__) 1190 std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n"; 1191#endif 1192 break; 1193 1194 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: 1195#if defined(__RTMIDI_DEBUG__) 1196 std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; 1197 std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" 1198 << (int) ev->data.connect.sender.port 1199 << ", dest = " << (int) ev->data.connect.dest.client << ":" 1200 << (int) ev->data.connect.dest.port 1201 << std::endl; 1202#endif 1203 break; 1204 1205 case SND_SEQ_EVENT_QFRAME: // MIDI time code 1206 if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; 1207 break; 1208 1209 case SND_SEQ_EVENT_TICK: // MIDI timing tick 1210 if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; 1211 break; 1212 1213 case SND_SEQ_EVENT_SENSING: // Active sensing 1214 if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; 1215 break; 1216 1217 case SND_SEQ_EVENT_SYSEX: 1218 if ( (data->ignoreFlags & 0x01) ) break; 1219 if ( ev->data.ext.len > apiData->bufferSize ) { 1220 apiData->bufferSize = ev->data.ext.len; 1221 free( buffer ); 1222 buffer = (unsigned char *) malloc( apiData->bufferSize ); 1223 if ( buffer == NULL ) { 1224 data->doInput = false; 1225 std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; 1226 break; 1227 } 1228 } 1229 1230 default: 1231 doDecode = true; 1232 } 1233 1234 if ( doDecode ) { 1235 1236 nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); 1237 if ( nBytes > 0 ) { 1238 // The ALSA sequencer has a maximum buffer size for MIDI sysex 1239 // events of 256 bytes. If a device sends sysex messages larger 1240 // than this, they are segmented into 256 byte chunks. So, 1241 // we'll watch for this and concatenate sysex chunks into a 1242 // single sysex message if necessary. 1243 if ( !continueSysex ) 1244 message.bytes.assign( buffer, &buffer[nBytes] ); 1245 else 1246 message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); 1247 1248 continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); 1249 if ( !continueSysex ) { 1250 1251 // Calculate the time stamp: 1252 message.timeStamp = 0.0; 1253 1254 // Method 1: Use the system time. 1255 //(void)gettimeofday(&tv, (struct timezone *)NULL); 1256 //time = (tv.tv_sec * 1000000) + tv.tv_usec; 1257 1258 // Method 2: Use the ALSA sequencer event time data. 1259 // (thanks to Pedro Lopez-Cabanillas!). 1260 time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); 1261 lastTime = time; 1262 time -= apiData->lastTime; 1263 apiData->lastTime = lastTime; 1264 if ( data->firstMessage == true ) 1265 data->firstMessage = false; 1266 else 1267 message.timeStamp = time * 0.000001; 1268 } 1269 else { 1270#if defined(__RTMIDI_DEBUG__) 1271 std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; 1272#endif 1273 } 1274 } 1275 } 1276 1277 snd_seq_free_event( ev ); 1278 if ( message.bytes.size() == 0 || continueSysex ) continue; 1279 1280 if ( data->usingCallback ) { 1281 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 1282 callback( message.timeStamp, &message.bytes, data->userData ); 1283 } 1284 else { 1285 // As long as we haven't reached our queue size limit, push the message. 1286 if ( data->queue.size < data->queue.ringSize ) { 1287 data->queue.ring[data->queue.back++] = message; 1288 if ( data->queue.back == data->queue.ringSize ) 1289 data->queue.back = 0; 1290 data->queue.size++; 1291 } 1292 else 1293 std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; 1294 } 1295 } 1296 1297 if ( buffer ) free( buffer ); 1298 snd_midi_event_free( apiData->coder ); 1299 apiData->coder = 0; 1300 apiData->thread = apiData->dummy_thread_id; 1301 return 0; 1302} 1303 1304MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) 1305{ 1306 initialize( clientName ); 1307} 1308 1309MidiInAlsa :: ~MidiInAlsa() 1310{ 1311 // Close a connection if it exists. 1312 closePort(); 1313 1314 // Shutdown the input thread. 1315 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); 1316 if ( inputData_.doInput ) { 1317 inputData_.doInput = false; 1318 int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) ); 1319 (void) res; 1320 if ( !pthread_equal(data->thread, data->dummy_thread_id) ) 1321 pthread_join( data->thread, NULL ); 1322 } 1323 1324 // Cleanup. 1325 close ( data->trigger_fds[0] ); 1326 close ( data->trigger_fds[1] ); 1327 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); 1328#ifndef AVOID_TIMESTAMPING 1329 snd_seq_free_queue( data->seq, data->queue_id ); 1330#endif 1331 freeSequencer(); 1332 delete data; 1333} 1334 1335void MidiInAlsa :: initialize( const std::string& clientName ) 1336{ 1337 snd_seq_t* seq = createSequencer( clientName ); 1338 if ( seq == NULL ) { 1339 s_seq = NULL; 1340 errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; 1341 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1342 } 1343 1344 // Save our api-specific connection information. 1345 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; 1346 data->seq = seq; 1347 data->portNum = -1; 1348 data->vport = -1; 1349 data->subscription = 0; 1350 data->dummy_thread_id = pthread_self(); 1351 data->thread = data->dummy_thread_id; 1352 data->trigger_fds[0] = -1; 1353 data->trigger_fds[1] = -1; 1354 apiData_ = (void *) data; 1355 inputData_.apiData = (void *) data; 1356 1357 if ( pipe(data->trigger_fds) == -1 ) { 1358 errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; 1359 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1360 } 1361 1362 // Create the input queue 1363#ifndef AVOID_TIMESTAMPING 1364 data->queue_id = snd_seq_alloc_named_queue(s_seq, "RtMidi Queue"); 1365 // Set arbitrary tempo (mm=100) and resolution (240) 1366 snd_seq_queue_tempo_t *qtempo; 1367 snd_seq_queue_tempo_alloca(&qtempo); 1368 snd_seq_queue_tempo_set_tempo(qtempo, 600000); 1369 snd_seq_queue_tempo_set_ppq(qtempo, 240); 1370 snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); 1371 snd_seq_drain_output(data->seq); 1372#endif 1373} 1374 1375// This function is used to count or get the pinfo structure for a given port number. 1376unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) 1377{ 1378 snd_seq_client_info_t *cinfo; 1379 int client; 1380 int count = 0; 1381 snd_seq_client_info_alloca( &cinfo ); 1382 1383 snd_seq_client_info_set_client( cinfo, -1 ); 1384 while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { 1385 client = snd_seq_client_info_get_client( cinfo ); 1386 if ( client == 0 ) continue; 1387 // Reset query info 1388 snd_seq_port_info_set_client( pinfo, client ); 1389 snd_seq_port_info_set_port( pinfo, -1 ); 1390 while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { 1391 unsigned int atyp = snd_seq_port_info_get_type( pinfo ); 1392 if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue; 1393 unsigned int caps = snd_seq_port_info_get_capability( pinfo ); 1394 if ( ( caps & type ) != type ) continue; 1395 if ( count == portNumber ) return 1; 1396 ++count; 1397 } 1398 } 1399 1400 // If a negative portNumber was used, return the port count. 1401 if ( portNumber < 0 ) return count; 1402 return 0; 1403} 1404 1405unsigned int MidiInAlsa :: getPortCount() 1406{ 1407 snd_seq_port_info_t *pinfo; 1408 snd_seq_port_info_alloca( &pinfo ); 1409 1410 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); 1411 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); 1412} 1413 1414std::string MidiInAlsa :: getPortName( unsigned int portNumber ) 1415{ 1416 snd_seq_client_info_t *cinfo; 1417 snd_seq_port_info_t *pinfo; 1418 snd_seq_client_info_alloca( &cinfo ); 1419 snd_seq_port_info_alloca( &pinfo ); 1420 1421 std::string stringName; 1422 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); 1423 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { 1424 int cnum = snd_seq_port_info_get_client( pinfo ); 1425 snd_seq_get_any_client_info( data->seq, cnum, cinfo ); 1426 std::ostringstream os; 1427 os << snd_seq_client_info_get_name( cinfo ); 1428 os << " "; // GO: These lines added to make sure devices are listed 1429 os << snd_seq_port_info_get_client( pinfo ); // GO: with full portnames added to ensure individual device names 1430 os << ":"; 1431 os << snd_seq_port_info_get_port( pinfo ); 1432 stringName = os.str(); 1433 return stringName; 1434 } 1435 1436 // If we get here, we didn't find a match. 1437 errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; 1438 RtMidi::error( RtError::WARNING, errorString_ ); 1439 return stringName; 1440 //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 1441} 1442 1443void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName ) 1444{ 1445 if ( connected_ ) { 1446 errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; 1447 RtMidi::error( RtError::WARNING, errorString_ ); 1448 return; 1449 } 1450 1451 unsigned int nSrc = this->getPortCount(); 1452 if (nSrc < 1) { 1453 errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; 1454 RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 1455 } 1456 1457 snd_seq_port_info_t *pinfo; 1458 snd_seq_port_info_alloca( &pinfo ); 1459 std::ostringstream ost; 1460 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); 1461 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { 1462 ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 1463 errorString_ = ost.str(); 1464 RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 1465 } 1466 1467 1468 snd_seq_addr_t sender, receiver; 1469 sender.client = snd_seq_port_info_get_client( pinfo ); 1470 sender.port = snd_seq_port_info_get_port( pinfo ); 1471 receiver.client = snd_seq_client_id( data->seq ); 1472 if ( data->vport < 0 ) { 1473 snd_seq_port_info_set_client( pinfo, 0 ); 1474 snd_seq_port_info_set_port( pinfo, 0 ); 1475 snd_seq_port_info_set_capability( pinfo, 1476 SND_SEQ_PORT_CAP_WRITE | 1477 SND_SEQ_PORT_CAP_SUBS_WRITE ); 1478 snd_seq_port_info_set_type( pinfo, 1479 SND_SEQ_PORT_TYPE_MIDI_GENERIC | 1480 SND_SEQ_PORT_TYPE_APPLICATION ); 1481 snd_seq_port_info_set_midi_channels(pinfo, 16); 1482#ifndef AVOID_TIMESTAMPING 1483 snd_seq_port_info_set_timestamping(pinfo, 1); 1484 snd_seq_port_info_set_timestamp_real(pinfo, 1); 1485 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); 1486#endif 1487 snd_seq_port_info_set_name(pinfo, portName.c_str() ); 1488 data->vport = snd_seq_create_port(data->seq, pinfo); 1489 1490 if ( data->vport < 0 ) { 1491 errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; 1492 RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1493 } 1494 } 1495 1496 receiver.port = data->vport;…
Large files files are truncated, but you can click here to view the full file