annotate data/midi/rtmidi/RtMidi.cpp @ 1412:b7a9edee85e0 scale-ticks

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