annotate CollidoscopeApp/src/RtMidi.cpp @ 18:f1ff1a81be20 tip

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