annotate data/midi/rtmidi/RtMidi.cpp @ 1773:fadd9f8aaa27

This output is too annoying, in the perfectly innocuous case of reading from an aggregate model whose components are different lengths
author Chris Cannam
date Wed, 14 Aug 2019 13:54:23 +0100
parents 48e9f538e6e9
children
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@1426 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@1426 1241 doDecode = true;
Chris@1426 1242 break;
Chris@559 1243
Chris@559 1244 default:
Chris@1397 1245 doDecode = true;
Chris@1397 1246 }
Chris@1397 1247
Chris@1397 1248 if ( doDecode ) {
Chris@1397 1249
Chris@559 1250 nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
Chris@1397 1251 if ( nBytes > 0 ) {
Chris@1397 1252 // The ALSA sequencer has a maximum buffer size for MIDI sysex
Chris@1397 1253 // events of 256 bytes. If a device sends sysex messages larger
Chris@1397 1254 // than this, they are segmented into 256 byte chunks. So,
Chris@1397 1255 // we'll watch for this and concatenate sysex chunks into a
Chris@1397 1256 // single sysex message if necessary.
Chris@1397 1257 if ( !continueSysex )
Chris@1397 1258 message.bytes.assign( buffer, &buffer[nBytes] );
Chris@1397 1259 else
Chris@1397 1260 message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
Chris@1397 1261
Chris@1397 1262 continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
Chris@1397 1263 if ( !continueSysex ) {
Chris@1397 1264
Chris@1397 1265 // Calculate the time stamp:
Chris@1397 1266 message.timeStamp = 0.0;
Chris@1397 1267
Chris@1397 1268 // Method 1: Use the system time.
Chris@1397 1269 //(void)gettimeofday(&tv, (struct timezone *)NULL);
Chris@1397 1270 //time = (tv.tv_sec * 1000000) + tv.tv_usec;
Chris@1397 1271
Chris@1397 1272 // Method 2: Use the ALSA sequencer event time data.
Chris@1397 1273 // (thanks to Pedro Lopez-Cabanillas!).
Chris@1397 1274 time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
Chris@1397 1275 lastTime = time;
Chris@1397 1276 time -= apiData->lastTime;
Chris@1397 1277 apiData->lastTime = lastTime;
Chris@1397 1278 if ( data->firstMessage == true )
Chris@1397 1279 data->firstMessage = false;
Chris@1397 1280 else
Chris@1397 1281 message.timeStamp = time * 0.000001;
Chris@1397 1282 }
Chris@1397 1283 else {
Chris@559 1284 #if defined(__RTMIDI_DEBUG__)
Chris@1397 1285 std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
Chris@559 1286 #endif
Chris@1397 1287 }
Chris@559 1288 }
Chris@559 1289 }
Chris@559 1290
Chris@1397 1291 snd_seq_free_event( ev );
Chris@1397 1292 if ( message.bytes.size() == 0 || continueSysex ) continue;
Chris@1397 1293
Chris@1397 1294 if ( data->usingCallback ) {
Chris@559 1295 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 1296 callback( message.timeStamp, &message.bytes, data->userData );
Chris@559 1297 }
Chris@559 1298 else {
Chris@559 1299 // As long as we haven't reached our queue size limit, push the message.
Chris@1397 1300 if ( data->queue.size < data->queue.ringSize ) {
Chris@1397 1301 data->queue.ring[data->queue.back++] = message;
Chris@1397 1302 if ( data->queue.back == data->queue.ringSize )
Chris@1397 1303 data->queue.back = 0;
Chris@1397 1304 data->queue.size++;
Chris@1397 1305 }
Chris@559 1306 else
Chris@1397 1307 std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n";
Chris@559 1308 }
Chris@559 1309 }
Chris@559 1310
Chris@559 1311 if ( buffer ) free( buffer );
Chris@559 1312 snd_midi_event_free( apiData->coder );
Chris@559 1313 apiData->coder = 0;
Chris@1397 1314 apiData->thread = apiData->dummy_thread_id;
Chris@559 1315 return 0;
Chris@559 1316 }
Chris@559 1317
Chris@1397 1318 MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
Chris@1397 1319 {
Chris@1397 1320 initialize( clientName );
Chris@1397 1321 }
Chris@1397 1322
Chris@1397 1323 MidiInAlsa :: ~MidiInAlsa()
Chris@1397 1324 {
Chris@1397 1325 // Close a connection if it exists.
Chris@1397 1326 closePort();
Chris@1397 1327
Chris@1397 1328 // Shutdown the input thread.
Chris@1397 1329 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@1397 1330 if ( inputData_.doInput ) {
Chris@1397 1331 inputData_.doInput = false;
Chris@1397 1332 int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );
Chris@1397 1333 (void) res;
Chris@1397 1334 if ( !pthread_equal(data->thread, data->dummy_thread_id) )
Chris@1397 1335 pthread_join( data->thread, NULL );
Chris@1397 1336 }
Chris@1397 1337
Chris@1397 1338 // Cleanup.
Chris@1397 1339 close ( data->trigger_fds[0] );
Chris@1397 1340 close ( data->trigger_fds[1] );
Chris@1397 1341 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
Chris@1397 1342 #ifndef AVOID_TIMESTAMPING
Chris@1397 1343 snd_seq_free_queue( data->seq, data->queue_id );
Chris@1397 1344 #endif
Chris@1397 1345 snd_seq_close( data->seq );
Chris@1397 1346 delete data;
Chris@1397 1347 }
Chris@1397 1348
Chris@1397 1349 void MidiInAlsa :: initialize( const std::string& clientName )
Chris@559 1350 {
Chris@559 1351 // Set up the ALSA sequencer client.
Chris@565 1352 snd_seq_t *seq;
Chris@559 1353 int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
Chris@559 1354 if ( result < 0 ) {
Chris@1397 1355 errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object.";
Chris@1397 1356 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1357 return;
Chris@1397 1358 }
Chris@559 1359
Chris@559 1360 // Set client name.
Chris@565 1361 snd_seq_set_client_name( seq, clientName.c_str() );
Chris@559 1362
Chris@559 1363 // Save our api-specific connection information.
Chris@559 1364 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
Chris@559 1365 data->seq = seq;
Chris@1397 1366 data->portNum = -1;
Chris@559 1367 data->vport = -1;
Chris@1397 1368 data->subscription = 0;
Chris@1397 1369 data->dummy_thread_id = pthread_self();
Chris@1397 1370 data->thread = data->dummy_thread_id;
Chris@1397 1371 data->trigger_fds[0] = -1;
Chris@1397 1372 data->trigger_fds[1] = -1;
Chris@559 1373 apiData_ = (void *) data;
Chris@559 1374 inputData_.apiData = (void *) data;
Chris@559 1375
Chris@1397 1376 if ( pipe(data->trigger_fds) == -1 ) {
Chris@1397 1377 errorString_ = "MidiInAlsa::initialize: error creating pipe objects.";
Chris@1397 1378 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1379 return;
Chris@1397 1380 }
Chris@1397 1381
Chris@559 1382 // Create the input queue
Chris@1397 1383 #ifndef AVOID_TIMESTAMPING
Chris@559 1384 data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue");
Chris@559 1385 // Set arbitrary tempo (mm=100) and resolution (240)
Chris@559 1386 snd_seq_queue_tempo_t *qtempo;
Chris@559 1387 snd_seq_queue_tempo_alloca(&qtempo);
Chris@559 1388 snd_seq_queue_tempo_set_tempo(qtempo, 600000);
Chris@559 1389 snd_seq_queue_tempo_set_ppq(qtempo, 240);
Chris@559 1390 snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo);
Chris@559 1391 snd_seq_drain_output(data->seq);
Chris@1397 1392 #endif
Chris@559 1393 }
Chris@559 1394
Chris@559 1395 // This function is used to count or get the pinfo structure for a given port number.
Chris@559 1396 unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )
Chris@559 1397 {
Chris@1397 1398 snd_seq_client_info_t *cinfo;
Chris@559 1399 int client;
Chris@559 1400 int count = 0;
Chris@1397 1401 snd_seq_client_info_alloca( &cinfo );
Chris@1397 1402
Chris@1397 1403 snd_seq_client_info_set_client( cinfo, -1 );
Chris@1397 1404 while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {
Chris@559 1405 client = snd_seq_client_info_get_client( cinfo );
Chris@559 1406 if ( client == 0 ) continue;
Chris@1397 1407 // Reset query info
Chris@1397 1408 snd_seq_port_info_set_client( pinfo, client );
Chris@1397 1409 snd_seq_port_info_set_port( pinfo, -1 );
Chris@1397 1410 while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
Chris@565 1411 unsigned int atyp = snd_seq_port_info_get_type( pinfo );
Chris@1397 1412 if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&
Chris@1397 1413 ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue;
Chris@565 1414 unsigned int caps = snd_seq_port_info_get_capability( pinfo );
Chris@565 1415 if ( ( caps & type ) != type ) continue;
Chris@559 1416 if ( count == portNumber ) return 1;
Chris@1397 1417 ++count;
Chris@1397 1418 }
Chris@1397 1419 }
Chris@559 1420
Chris@559 1421 // If a negative portNumber was used, return the port count.
Chris@559 1422 if ( portNumber < 0 ) return count;
Chris@559 1423 return 0;
Chris@559 1424 }
Chris@559 1425
Chris@1397 1426 unsigned int MidiInAlsa :: getPortCount()
Chris@1397 1427 {
Chris@1397 1428 snd_seq_port_info_t *pinfo;
Chris@1397 1429 snd_seq_port_info_alloca( &pinfo );
Chris@1397 1430
Chris@1397 1431 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@1397 1432 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );
Chris@1397 1433 }
Chris@1397 1434
Chris@1397 1435 std::string MidiInAlsa :: getPortName( unsigned int portNumber )
Chris@1397 1436 {
Chris@1397 1437 snd_seq_client_info_t *cinfo;
Chris@1397 1438 snd_seq_port_info_t *pinfo;
Chris@1397 1439 snd_seq_client_info_alloca( &cinfo );
Chris@1397 1440 snd_seq_port_info_alloca( &pinfo );
Chris@1397 1441
Chris@1397 1442 std::string stringName;
Chris@1397 1443 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@1397 1444 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
Chris@1397 1445 int cnum = snd_seq_port_info_get_client( pinfo );
Chris@1397 1446 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
Chris@1397 1447 std::ostringstream os;
Chris@1397 1448 os << snd_seq_client_info_get_name( cinfo );
Chris@1397 1449 os << " "; // These lines added to make sure devices are listed
Chris@1397 1450 os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names
Chris@1397 1451 os << ":";
Chris@1397 1452 os << snd_seq_port_info_get_port( pinfo );
Chris@1397 1453 stringName = os.str();
Chris@1397 1454 return stringName;
Chris@1397 1455 }
Chris@1397 1456
Chris@1397 1457 // If we get here, we didn't find a match.
Chris@1397 1458 errorString_ = "MidiInAlsa::getPortName: error looking for port name!";
Chris@1397 1459 error( RtMidiError::WARNING, errorString_ );
Chris@1397 1460 return stringName;
Chris@1397 1461 }
Chris@1397 1462
Chris@1397 1463 void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName )
Chris@559 1464 {
Chris@559 1465 if ( connected_ ) {
Chris@1397 1466 errorString_ = "MidiInAlsa::openPort: a valid connection already exists!";
Chris@1397 1467 error( RtMidiError::WARNING, errorString_ );
Chris@559 1468 return;
Chris@559 1469 }
Chris@559 1470
Chris@559 1471 unsigned int nSrc = this->getPortCount();
Chris@1397 1472 if ( nSrc < 1 ) {
Chris@1397 1473 errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!";
Chris@1397 1474 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
Chris@1397 1475 return;
Chris@559 1476 }
Chris@559 1477
Chris@1397 1478 snd_seq_port_info_t *src_pinfo;
Chris@1397 1479 snd_seq_port_info_alloca( &src_pinfo );
Chris@559 1480 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@1397 1481 if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {
Chris@1397 1482 std::ostringstream ost;
Chris@1397 1483 ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1484 errorString_ = ost.str();
Chris@1397 1485 error( RtMidiError::INVALID_PARAMETER, errorString_ );
Chris@1397 1486 return;
Chris@559 1487 }
Chris@559 1488
Chris@559 1489 snd_seq_addr_t sender, receiver;
Chris@1397 1490 sender.client = snd_seq_port_info_get_client( src_pinfo );
Chris@1397 1491 sender.port = snd_seq_port_info_get_port( src_pinfo );
Chris@1397 1492 receiver.client = snd_seq_client_id( data->seq );
Chris@1397 1493
Chris@1397 1494 snd_seq_port_info_t *pinfo;
Chris@1397 1495 snd_seq_port_info_alloca( &pinfo );
Chris@559 1496 if ( data->vport < 0 ) {
Chris@559 1497 snd_seq_port_info_set_client( pinfo, 0 );
Chris@559 1498 snd_seq_port_info_set_port( pinfo, 0 );
Chris@559 1499 snd_seq_port_info_set_capability( pinfo,
Chris@559 1500 SND_SEQ_PORT_CAP_WRITE |
Chris@559 1501 SND_SEQ_PORT_CAP_SUBS_WRITE );
Chris@559 1502 snd_seq_port_info_set_type( pinfo,
Chris@559 1503 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
Chris@559 1504 SND_SEQ_PORT_TYPE_APPLICATION );
Chris@559 1505 snd_seq_port_info_set_midi_channels(pinfo, 16);
Chris@1397 1506 #ifndef AVOID_TIMESTAMPING
Chris@559 1507 snd_seq_port_info_set_timestamping(pinfo, 1);
Chris@559 1508 snd_seq_port_info_set_timestamp_real(pinfo, 1);
Chris@559 1509 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
Chris@1397 1510 #endif
Chris@565 1511 snd_seq_port_info_set_name(pinfo, portName.c_str() );
Chris@559 1512 data->vport = snd_seq_create_port(data->seq, pinfo);
Chris@559 1513
Chris@559 1514 if ( data->vport < 0 ) {
Chris@1397 1515 errorString_ = "MidiInAlsa::openPort: ALSA error creating input port.";
Chris@1397 1516 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1517 return;
Chris@1397 1518 }
Chris@1397 1519 data->vport = snd_seq_port_info_get_port(pinfo);
Chris@1397 1520 }
Chris@1397 1521
Chris@1397 1522 receiver.port = data->vport;
Chris@1397 1523
Chris@1397 1524 if ( !data->subscription ) {
Chris@1397 1525 // Make subscription
Chris@1397 1526 if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {
Chris@1397 1527 errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription.";
Chris@1397 1528 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1529 return;
Chris@1397 1530 }
Chris@1397 1531 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
Chris@1397 1532 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
Chris@1397 1533 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
Chris@1397 1534 snd_seq_port_subscribe_free( data->subscription );
Chris@1397 1535 data->subscription = 0;
Chris@1397 1536 errorString_ = "MidiInAlsa::openPort: ALSA error making port connection.";
Chris@1397 1537 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1538 return;
Chris@559 1539 }
Chris@559 1540 }
Chris@559 1541
Chris@559 1542 if ( inputData_.doInput == false ) {
Chris@559 1543 // Start the input queue
Chris@1397 1544 #ifndef AVOID_TIMESTAMPING
Chris@559 1545 snd_seq_start_queue( data->seq, data->queue_id, NULL );
Chris@559 1546 snd_seq_drain_output( data->seq );
Chris@1397 1547 #endif
Chris@559 1548 // Start our MIDI input thread.
Chris@559 1549 pthread_attr_t attr;
Chris@559 1550 pthread_attr_init(&attr);
Chris@559 1551 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
Chris@559 1552 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
Chris@559 1553
Chris@559 1554 inputData_.doInput = true;
Chris@559 1555 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
Chris@559 1556 pthread_attr_destroy(&attr);
Chris@1397 1557 if ( err ) {
Chris@559 1558 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@559 1559 snd_seq_port_subscribe_free( data->subscription );
Chris@1397 1560 data->subscription = 0;
Chris@559 1561 inputData_.doInput = false;
Chris@1397 1562 errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
Chris@1397 1563 error( RtMidiError::THREAD_ERROR, errorString_ );
Chris@1397 1564 return;
Chris@559 1565 }
Chris@559 1566 }
Chris@559 1567
Chris@559 1568 connected_ = true;
Chris@559 1569 }
Chris@559 1570
Chris@1397 1571 void MidiInAlsa :: openVirtualPort( std::string portName )
Chris@559 1572 {
Chris@559 1573 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1574 if ( data->vport < 0 ) {
Chris@559 1575 snd_seq_port_info_t *pinfo;
Chris@559 1576 snd_seq_port_info_alloca( &pinfo );
Chris@559 1577 snd_seq_port_info_set_capability( pinfo,
Chris@1429 1578 SND_SEQ_PORT_CAP_WRITE |
Chris@1429 1579 SND_SEQ_PORT_CAP_SUBS_WRITE );
Chris@559 1580 snd_seq_port_info_set_type( pinfo,
Chris@1429 1581 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
Chris@1429 1582 SND_SEQ_PORT_TYPE_APPLICATION );
Chris@559 1583 snd_seq_port_info_set_midi_channels(pinfo, 16);
Chris@1397 1584 #ifndef AVOID_TIMESTAMPING
Chris@559 1585 snd_seq_port_info_set_timestamping(pinfo, 1);
Chris@559 1586 snd_seq_port_info_set_timestamp_real(pinfo, 1);
Chris@559 1587 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
Chris@1397 1588 #endif
Chris@559 1589 snd_seq_port_info_set_name(pinfo, portName.c_str());
Chris@559 1590 data->vport = snd_seq_create_port(data->seq, pinfo);
Chris@559 1591
Chris@559 1592 if ( data->vport < 0 ) {
Chris@1397 1593 errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port.";
Chris@1397 1594 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1595 return;
Chris@559 1596 }
Chris@1397 1597 data->vport = snd_seq_port_info_get_port(pinfo);
Chris@559 1598 }
Chris@559 1599
Chris@559 1600 if ( inputData_.doInput == false ) {
Chris@1397 1601 // Wait for old thread to stop, if still running
Chris@1397 1602 if ( !pthread_equal(data->thread, data->dummy_thread_id) )
Chris@1397 1603 pthread_join( data->thread, NULL );
Chris@1397 1604
Chris@559 1605 // Start the input queue
Chris@1397 1606 #ifndef AVOID_TIMESTAMPING
Chris@559 1607 snd_seq_start_queue( data->seq, data->queue_id, NULL );
Chris@559 1608 snd_seq_drain_output( data->seq );
Chris@1397 1609 #endif
Chris@559 1610 // Start our MIDI input thread.
Chris@559 1611 pthread_attr_t attr;
Chris@559 1612 pthread_attr_init(&attr);
Chris@559 1613 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
Chris@559 1614 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
Chris@559 1615
Chris@559 1616 inputData_.doInput = true;
Chris@559 1617 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
Chris@559 1618 pthread_attr_destroy(&attr);
Chris@1397 1619 if ( err ) {
Chris@1397 1620 if ( data->subscription ) {
Chris@1397 1621 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@1397 1622 snd_seq_port_subscribe_free( data->subscription );
Chris@1397 1623 data->subscription = 0;
Chris@1397 1624 }
Chris@559 1625 inputData_.doInput = false;
Chris@1397 1626 errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
Chris@1397 1627 error( RtMidiError::THREAD_ERROR, errorString_ );
Chris@1397 1628 return;
Chris@559 1629 }
Chris@559 1630 }
Chris@559 1631 }
Chris@559 1632
Chris@1397 1633 void MidiInAlsa :: closePort( void )
Chris@559 1634 {
Chris@1397 1635 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@1397 1636
Chris@559 1637 if ( connected_ ) {
Chris@1397 1638 if ( data->subscription ) {
Chris@1397 1639 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@1397 1640 snd_seq_port_subscribe_free( data->subscription );
Chris@1397 1641 data->subscription = 0;
Chris@1397 1642 }
Chris@559 1643 // Stop the input queue
Chris@1397 1644 #ifndef AVOID_TIMESTAMPING
Chris@559 1645 snd_seq_stop_queue( data->seq, data->queue_id, NULL );
Chris@559 1646 snd_seq_drain_output( data->seq );
Chris@1397 1647 #endif
Chris@559 1648 connected_ = false;
Chris@559 1649 }
Chris@1397 1650
Chris@1397 1651 // Stop thread to avoid triggering the callback, while the port is intended to be closed
Chris@1397 1652 if ( inputData_.doInput ) {
Chris@1397 1653 inputData_.doInput = false;
Chris@1397 1654 int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );
Chris@1397 1655 (void) res;
Chris@1397 1656 if ( !pthread_equal(data->thread, data->dummy_thread_id) )
Chris@1397 1657 pthread_join( data->thread, NULL );
Chris@1397 1658 }
Chris@559 1659 }
Chris@559 1660
Chris@1397 1661 //*********************************************************************//
Chris@1397 1662 // API: LINUX ALSA
Chris@1397 1663 // Class Definitions: MidiOutAlsa
Chris@1397 1664 //*********************************************************************//
Chris@1397 1665
Chris@1397 1666 MidiOutAlsa :: MidiOutAlsa( const std::string clientName ) : MidiOutApi()
Chris@1397 1667 {
Chris@1397 1668 initialize( clientName );
Chris@1397 1669 }
Chris@1397 1670
Chris@1397 1671 MidiOutAlsa :: ~MidiOutAlsa()
Chris@559 1672 {
Chris@559 1673 // Close a connection if it exists.
Chris@559 1674 closePort();
Chris@559 1675
Chris@1397 1676 // Cleanup.
Chris@559 1677 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1678 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
Chris@1397 1679 if ( data->coder ) snd_midi_event_free( data->coder );
Chris@1397 1680 if ( data->buffer ) free( data->buffer );
Chris@559 1681 snd_seq_close( data->seq );
Chris@559 1682 delete data;
Chris@559 1683 }
Chris@559 1684
Chris@1397 1685 void MidiOutAlsa :: initialize( const std::string& clientName )
Chris@1397 1686 {
Chris@1397 1687 // Set up the ALSA sequencer client.
Chris@1397 1688 snd_seq_t *seq;
Chris@1397 1689 int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
Chris@1397 1690 if ( result1 < 0 ) {
Chris@1397 1691 errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object.";
Chris@1397 1692 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1693 return;
Chris@1429 1694 }
Chris@1397 1695
Chris@1397 1696 // Set client name.
Chris@1397 1697 snd_seq_set_client_name( seq, clientName.c_str() );
Chris@1397 1698
Chris@1397 1699 // Save our api-specific connection information.
Chris@1397 1700 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
Chris@1397 1701 data->seq = seq;
Chris@1397 1702 data->portNum = -1;
Chris@1397 1703 data->vport = -1;
Chris@1397 1704 data->bufferSize = 32;
Chris@1397 1705 data->coder = 0;
Chris@1397 1706 data->buffer = 0;
Chris@1397 1707 int result = snd_midi_event_new( data->bufferSize, &data->coder );
Chris@1397 1708 if ( result < 0 ) {
Chris@1397 1709 delete data;
Chris@1397 1710 errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n";
Chris@1397 1711 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1712 return;
Chris@1397 1713 }
Chris@1397 1714 data->buffer = (unsigned char *) malloc( data->bufferSize );
Chris@1397 1715 if ( data->buffer == NULL ) {
Chris@1397 1716 delete data;
Chris@1397 1717 errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
Chris@1397 1718 error( RtMidiError::MEMORY_ERROR, errorString_ );
Chris@1397 1719 return;
Chris@1397 1720 }
Chris@1397 1721 snd_midi_event_init( data->coder );
Chris@1397 1722 apiData_ = (void *) data;
Chris@1397 1723 }
Chris@1397 1724
Chris@1397 1725 unsigned int MidiOutAlsa :: getPortCount()
Chris@559 1726 {
Chris@1429 1727 snd_seq_port_info_t *pinfo;
Chris@1429 1728 snd_seq_port_info_alloca( &pinfo );
Chris@559 1729
Chris@559 1730 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@1397 1731 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );
Chris@559 1732 }
Chris@559 1733
Chris@1397 1734 std::string MidiOutAlsa :: getPortName( unsigned int portNumber )
Chris@559 1735 {
Chris@565 1736 snd_seq_client_info_t *cinfo;
Chris@565 1737 snd_seq_port_info_t *pinfo;
Chris@565 1738 snd_seq_client_info_alloca( &cinfo );
Chris@565 1739 snd_seq_port_info_alloca( &pinfo );
Chris@559 1740
Chris@1397 1741 std::string stringName;
Chris@559 1742 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1743 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
Chris@565 1744 int cnum = snd_seq_port_info_get_client(pinfo);
Chris@565 1745 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
Chris@565 1746 std::ostringstream os;
Chris@565 1747 os << snd_seq_client_info_get_name(cinfo);
Chris@1397 1748 os << " "; // These lines added to make sure devices are listed
Chris@1397 1749 os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names
Chris@565 1750 os << ":";
Chris@565 1751 os << snd_seq_port_info_get_port(pinfo);
Chris@1397 1752 stringName = os.str();
Chris@559 1753 return stringName;
Chris@559 1754 }
Chris@559 1755
Chris@559 1756 // If we get here, we didn't find a match.
Chris@1397 1757 errorString_ = "MidiOutAlsa::getPortName: error looking for port name!";
Chris@1397 1758 error( RtMidiError::WARNING, errorString_ );
Chris@1397 1759 return stringName;
Chris@559 1760 }
Chris@559 1761
Chris@1397 1762 void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string portName )
Chris@559 1763 {
Chris@559 1764 if ( connected_ ) {
Chris@1397 1765 errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!";
Chris@1397 1766 error( RtMidiError::WARNING, errorString_ );
Chris@559 1767 return;
Chris@559 1768 }
Chris@559 1769
Chris@559 1770 unsigned int nSrc = this->getPortCount();
Chris@559 1771 if (nSrc < 1) {
Chris@1397 1772 errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!";
Chris@1397 1773 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
Chris@1397 1774 return;
Chris@559 1775 }
Chris@559 1776
Chris@1429 1777 snd_seq_port_info_t *pinfo;
Chris@1429 1778 snd_seq_port_info_alloca( &pinfo );
Chris@559 1779 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1780 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {
Chris@1397 1781 std::ostringstream ost;
Chris@1397 1782 ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1783 errorString_ = ost.str();
Chris@1397 1784 error( RtMidiError::INVALID_PARAMETER, errorString_ );
Chris@1397 1785 return;
Chris@559 1786 }
Chris@559 1787
Chris@559 1788 snd_seq_addr_t sender, receiver;
Chris@1397 1789 receiver.client = snd_seq_port_info_get_client( pinfo );
Chris@1397 1790 receiver.port = snd_seq_port_info_get_port( pinfo );
Chris@1397 1791 sender.client = snd_seq_client_id( data->seq );
Chris@559 1792
Chris@559 1793 if ( data->vport < 0 ) {
Chris@565 1794 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
Chris@559 1795 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
Chris@1397 1796 SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
Chris@559 1797 if ( data->vport < 0 ) {
Chris@1397 1798 errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port.";
Chris@1397 1799 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1800 return;
Chris@559 1801 }
Chris@559 1802 }
Chris@559 1803
Chris@1397 1804 sender.port = data->vport;
Chris@559 1805
Chris@559 1806 // Make subscription
Chris@1397 1807 if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {
Chris@1397 1808 snd_seq_port_subscribe_free( data->subscription );
Chris@1397 1809 errorString_ = "MidiOutAlsa::openPort: error allocating port subscription.";
Chris@1397 1810 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1811 return;
Chris@1397 1812 }
Chris@559 1813 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
Chris@559 1814 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
Chris@559 1815 snd_seq_port_subscribe_set_time_update(data->subscription, 1);
Chris@559 1816 snd_seq_port_subscribe_set_time_real(data->subscription, 1);
Chris@559 1817 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
Chris@1397 1818 snd_seq_port_subscribe_free( data->subscription );
Chris@1397 1819 errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection.";
Chris@1397 1820 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1821 return;
Chris@559 1822 }
Chris@559 1823
Chris@559 1824 connected_ = true;
Chris@559 1825 }
Chris@559 1826
Chris@1397 1827 void MidiOutAlsa :: closePort( void )
Chris@559 1828 {
Chris@559 1829 if ( connected_ ) {
Chris@559 1830 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1831 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@559 1832 snd_seq_port_subscribe_free( data->subscription );
Chris@559 1833 connected_ = false;
Chris@559 1834 }
Chris@559 1835 }
Chris@559 1836
Chris@1397 1837 void MidiOutAlsa :: openVirtualPort( std::string portName )
Chris@559 1838 {
Chris@559 1839 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1840 if ( data->vport < 0 ) {
Chris@559 1841 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
Chris@559 1842 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
Chris@1397 1843 SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
Chris@559 1844
Chris@559 1845 if ( data->vport < 0 ) {
Chris@1397 1846 errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port.";
Chris@1397 1847 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@559 1848 }
Chris@559 1849 }
Chris@559 1850 }
Chris@559 1851
Chris@1397 1852 void MidiOutAlsa :: sendMessage( std::vector<unsigned char> *message )
Chris@559 1853 {
Chris@559 1854 int result;
Chris@559 1855 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@1397 1856 unsigned int nBytes = message->size();
Chris@559 1857 if ( nBytes > data->bufferSize ) {
Chris@559 1858 data->bufferSize = nBytes;
Chris@559 1859 result = snd_midi_event_resize_buffer ( data->coder, nBytes);
Chris@559 1860 if ( result != 0 ) {
Chris@1397 1861 errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer.";
Chris@1397 1862 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 1863 return;
Chris@559 1864 }
Chris@559 1865 free (data->buffer);
Chris@559 1866 data->buffer = (unsigned char *) malloc( data->bufferSize );
Chris@559 1867 if ( data->buffer == NULL ) {
Chris@1397 1868 errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
Chris@1397 1869 error( RtMidiError::MEMORY_ERROR, errorString_ );
Chris@1397 1870 return;
Chris@559 1871 }
Chris@559 1872 }
Chris@559 1873
Chris@559 1874 snd_seq_event_t ev;
Chris@559 1875 snd_seq_ev_clear(&ev);
Chris@1397 1876 snd_seq_ev_set_source(&ev, data->vport);
Chris@559 1877 snd_seq_ev_set_subs(&ev);
Chris@559 1878 snd_seq_ev_set_direct(&ev);
Chris@1397 1879 for ( unsigned int i=0; i<nBytes; ++i ) data->buffer[i] = message->at(i);
Chris@1397 1880 result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
Chris@559 1881 if ( result < (int)nBytes ) {
Chris@1397 1882 errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
Chris@1397 1883 error( RtMidiError::WARNING, errorString_ );
Chris@559 1884 return;
Chris@559 1885 }
Chris@559 1886
Chris@559 1887 // Send the event.
Chris@559 1888 result = snd_seq_event_output(data->seq, &ev);
Chris@559 1889 if ( result < 0 ) {
Chris@1397 1890 errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port.";
Chris@1397 1891 error( RtMidiError::WARNING, errorString_ );
Chris@1397 1892 return;
Chris@559 1893 }
Chris@559 1894 snd_seq_drain_output(data->seq);
Chris@559 1895 }
Chris@559 1896
Chris@559 1897 #endif // __LINUX_ALSA__
Chris@559 1898
Chris@559 1899
Chris@559 1900 //*********************************************************************//
Chris@559 1901 // API: Windows Multimedia Library (MM)
Chris@559 1902 //*********************************************************************//
Chris@559 1903
Chris@559 1904 // API information deciphered from:
Chris@559 1905 // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
Chris@559 1906
Chris@559 1907 // Thanks to Jean-Baptiste Berruchon for the sysex code.
Chris@559 1908
Chris@559 1909 #if defined(__WINDOWS_MM__)
Chris@559 1910
Chris@559 1911 // The Windows MM API is based on the use of a callback function for
Chris@559 1912 // MIDI input. We convert the system specific time stamps to delta
Chris@559 1913 // time values.
Chris@559 1914
Chris@559 1915 // Windows MM MIDI header files.
Chris@559 1916 #include <windows.h>
Chris@559 1917 #include <mmsystem.h>
Chris@559 1918
Chris@1397 1919 #define RT_SYSEX_BUFFER_SIZE 1024
Chris@1397 1920 #define RT_SYSEX_BUFFER_COUNT 4
Chris@1397 1921
Chris@559 1922 // A structure to hold variables related to the CoreMIDI API
Chris@559 1923 // implementation.
Chris@559 1924 struct WinMidiData {
Chris@559 1925 HMIDIIN inHandle; // Handle to Midi Input Device
Chris@559 1926 HMIDIOUT outHandle; // Handle to Midi Output Device
Chris@559 1927 DWORD lastTime;
Chris@1397 1928 MidiInApi::MidiMessage message;
Chris@1397 1929 LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];
Chris@1397 1930 CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
Chris@559 1931 };
Chris@559 1932
Chris@559 1933 //*********************************************************************//
Chris@559 1934 // API: Windows MM
Chris@1397 1935 // Class Definitions: MidiInWinMM
Chris@559 1936 //*********************************************************************//
Chris@559 1937
Chris@1397 1938 static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/,
Chris@559 1939 UINT inputStatus,
Chris@1397 1940 DWORD_PTR instancePtr,
Chris@1397 1941 DWORD_PTR midiMessage,
Chris@559 1942 DWORD timestamp )
Chris@559 1943 {
Chris@1397 1944 if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;
Chris@1397 1945
Chris@1397 1946 //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr);
Chris@1397 1947 MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr;
Chris@559 1948 WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);
Chris@559 1949
Chris@559 1950 // Calculate time stamp.
Chris@1397 1951 if ( data->firstMessage == true ) {
Chris@1397 1952 apiData->message.timeStamp = 0.0;
Chris@1397 1953 data->firstMessage = false;
Chris@1397 1954 }
Chris@559 1955 else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;
Chris@559 1956 apiData->lastTime = timestamp;
Chris@559 1957
Chris@559 1958 if ( inputStatus == MIM_DATA ) { // Channel or system message
Chris@559 1959
Chris@559 1960 // Make sure the first byte is a status byte.
Chris@559 1961 unsigned char status = (unsigned char) (midiMessage & 0x000000FF);
Chris@559 1962 if ( !(status & 0x80) ) return;
Chris@559 1963
Chris@559 1964 // Determine the number of bytes in the MIDI message.
Chris@559 1965 unsigned short nBytes = 1;
Chris@559 1966 if ( status < 0xC0 ) nBytes = 3;
Chris@559 1967 else if ( status < 0xE0 ) nBytes = 2;
Chris@559 1968 else if ( status < 0xF0 ) nBytes = 3;
Chris@1397 1969 else if ( status == 0xF1 ) {
Chris@1397 1970 if ( data->ignoreFlags & 0x02 ) return;
Chris@1397 1971 else nBytes = 2;
Chris@559 1972 }
Chris@1397 1973 else if ( status == 0xF2 ) nBytes = 3;
Chris@559 1974 else if ( status == 0xF3 ) nBytes = 2;
Chris@559 1975 else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {
Chris@559 1976 // A MIDI timing tick message and we're ignoring it.
Chris@559 1977 return;
Chris@559 1978 }
Chris@559 1979 else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
Chris@559 1980 // A MIDI active sensing message and we're ignoring it.
Chris@559 1981 return;
Chris@559 1982 }
Chris@559 1983
Chris@559 1984 // Copy bytes to our MIDI message.
Chris@559 1985 unsigned char *ptr = (unsigned char *) &midiMessage;
Chris@1397 1986 for ( int i=0; i<nBytes; ++i ) apiData->message.bytes.push_back( *ptr++ );
Chris@559 1987 }
Chris@1397 1988 else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )
Chris@565 1989 MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
Chris@1397 1990 if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) {
Chris@565 1991 // Sysex message and we're not ignoring it
Chris@1397 1992 for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i )
Chris@565 1993 apiData->message.bytes.push_back( sysex->lpData[i] );
Chris@565 1994 }
Chris@559 1995
Chris@565 1996 // The WinMM API requires that the sysex buffer be requeued after
Chris@565 1997 // input of each sysex message. Even if we are ignoring sysex
Chris@565 1998 // messages, we still need to requeue the buffer in case the user
Chris@565 1999 // decides to not ignore sysex messages in the future. However,
Chris@565 2000 // it seems that WinMM calls this function with an empty sysex
Chris@565 2001 // buffer when an application closes and in this case, we should
Chris@565 2002 // avoid requeueing it, else the computer suddenly reboots after
Chris@565 2003 // one or two minutes.
Chris@1397 2004 if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {
Chris@565 2005 //if ( sysex->dwBytesRecorded > 0 ) {
Chris@1397 2006 EnterCriticalSection( &(apiData->_mutex) );
Chris@1397 2007 MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );
Chris@1397 2008 LeaveCriticalSection( &(apiData->_mutex) );
Chris@559 2009 if ( result != MMSYSERR_NOERROR )
Chris@1397 2010 std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
Chris@565 2011
Chris@565 2012 if ( data->ignoreFlags & 0x01 ) return;
Chris@559 2013 }
Chris@565 2014 else return;
Chris@559 2015 }
Chris@559 2016
Chris@559 2017 if ( data->usingCallback ) {
Chris@559 2018 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 2019 callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );
Chris@559 2020 }
Chris@559 2021 else {
Chris@559 2022 // As long as we haven't reached our queue size limit, push the message.
Chris@1397 2023 if ( data->queue.size < data->queue.ringSize ) {
Chris@1397 2024 data->queue.ring[data->queue.back++] = apiData->message;
Chris@1397 2025 if ( data->queue.back == data->queue.ringSize )
Chris@1397 2026 data->queue.back = 0;
Chris@1397 2027 data->queue.size++;
Chris@1397 2028 }
Chris@559 2029 else
Chris@1397 2030 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
Chris@559 2031 }
Chris@559 2032
Chris@565 2033 // Clear the vector for the next input message.
Chris@559 2034 apiData->message.bytes.clear();
Chris@559 2035 }
Chris@559 2036
Chris@1397 2037 MidiInWinMM :: MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
Chris@1397 2038 {
Chris@1397 2039 initialize( clientName );
Chris@1397 2040 }
Chris@1397 2041
Chris@1397 2042 MidiInWinMM :: ~MidiInWinMM()
Chris@1397 2043 {
Chris@1397 2044 // Close a connection if it exists.
Chris@1397 2045 closePort();
Chris@1397 2046
Chris@1397 2047 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@1397 2048 DeleteCriticalSection( &(data->_mutex) );
Chris@1397 2049
Chris@1397 2050 // Cleanup.
Chris@1397 2051 delete data;
Chris@1397 2052 }
Chris@1397 2053
Chris@1397 2054 void MidiInWinMM :: initialize( const std::string& /*clientName*/ )
Chris@559 2055 {
Chris@559 2056 // We'll issue a warning here if no devices are available but not
Chris@559 2057 // throw an error since the user can plugin something later.
Chris@559 2058 unsigned int nDevices = midiInGetNumDevs();
Chris@559 2059 if ( nDevices == 0 ) {
Chris@1397 2060 errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available.";
Chris@1397 2061 error( RtMidiError::WARNING, errorString_ );
Chris@559 2062 }
Chris@559 2063
Chris@559 2064 // Save our api-specific connection information.
Chris@559 2065 WinMidiData *data = (WinMidiData *) new WinMidiData;
Chris@559 2066 apiData_ = (void *) data;
Chris@559 2067 inputData_.apiData = (void *) data;
Chris@559 2068 data->message.bytes.clear(); // needs to be empty for first input message
Chris@1397 2069
Chris@1397 2070 if ( !InitializeCriticalSectionAndSpinCount(&(data->_mutex), 0x00000400) ) {
Chris@1397 2071 errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed.";
Chris@1397 2072 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2073 }
Chris@559 2074 }
Chris@559 2075
Chris@1397 2076 void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@559 2077 {
Chris@559 2078 if ( connected_ ) {
Chris@1397 2079 errorString_ = "MidiInWinMM::openPort: a valid connection already exists!";
Chris@1397 2080 error( RtMidiError::WARNING, errorString_ );
Chris@559 2081 return;
Chris@559 2082 }
Chris@559 2083
Chris@559 2084 unsigned int nDevices = midiInGetNumDevs();
Chris@559 2085 if (nDevices == 0) {
Chris@1397 2086 errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!";
Chris@1397 2087 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
Chris@1397 2088 return;
Chris@559 2089 }
Chris@559 2090
Chris@559 2091 if ( portNumber >= nDevices ) {
Chris@1397 2092 std::ostringstream ost;
Chris@1397 2093 ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 2094 errorString_ = ost.str();
Chris@1397 2095 error( RtMidiError::INVALID_PARAMETER, errorString_ );
Chris@1397 2096 return;
Chris@559 2097 }
Chris@559 2098
Chris@559 2099 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2100 MMRESULT result = midiInOpen( &data->inHandle,
Chris@559 2101 portNumber,
Chris@1397 2102 (DWORD_PTR)&midiInputCallback,
Chris@1397 2103 (DWORD_PTR)&inputData_,
Chris@559 2104 CALLBACK_FUNCTION );
Chris@559 2105 if ( result != MMSYSERR_NOERROR ) {
Chris@1397 2106 errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port.";
Chris@1397 2107 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2108 return;
Chris@559 2109 }
Chris@559 2110
Chris@1397 2111 // Allocate and init the sysex buffers.
Chris@1397 2112 for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
Chris@1397 2113 data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
Chris@1397 2114 data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
Chris@1397 2115 data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
Chris@1397 2116 data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
Chris@1397 2117 data->sysexBuffer[i]->dwFlags = 0;
Chris@1397 2118
Chris@1397 2119 result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
Chris@1397 2120 if ( result != MMSYSERR_NOERROR ) {
Chris@1397 2121 midiInClose( data->inHandle );
Chris@1397 2122 errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
Chris@1397 2123 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2124 return;
Chris@1397 2125 }
Chris@1397 2126
Chris@1397 2127 // Register the buffer.
Chris@1397 2128 result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
Chris@1397 2129 if ( result != MMSYSERR_NOERROR ) {
Chris@1397 2130 midiInClose( data->inHandle );
Chris@1397 2131 errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer).";
Chris@1397 2132 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2133 return;
Chris@1397 2134 }
Chris@559 2135 }
Chris@559 2136
Chris@559 2137 result = midiInStart( data->inHandle );
Chris@559 2138 if ( result != MMSYSERR_NOERROR ) {
Chris@559 2139 midiInClose( data->inHandle );
Chris@1397 2140 errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port.";
Chris@1397 2141 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2142 return;
Chris@559 2143 }
Chris@559 2144
Chris@559 2145 connected_ = true;
Chris@559 2146 }
Chris@559 2147
Chris@1397 2148 void MidiInWinMM :: openVirtualPort( std::string /*portName*/ )
Chris@559 2149 {
Chris@559 2150 // This function cannot be implemented for the Windows MM MIDI API.
Chris@1397 2151 errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
Chris@1397 2152 error( RtMidiError::WARNING, errorString_ );
Chris@559 2153 }
Chris@559 2154
Chris@1397 2155 void MidiInWinMM :: closePort( void )
Chris@559 2156 {
Chris@559 2157 if ( connected_ ) {
Chris@559 2158 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@1397 2159 EnterCriticalSection( &(data->_mutex) );
Chris@559 2160 midiInReset( data->inHandle );
Chris@559 2161 midiInStop( data->inHandle );
Chris@559 2162
Chris@1397 2163 for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
Chris@1397 2164 int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
Chris@1397 2165 delete [] data->sysexBuffer[i]->lpData;
Chris@1397 2166 delete [] data->sysexBuffer[i];
Chris@1397 2167 if ( result != MMSYSERR_NOERROR ) {
Chris@1397 2168 midiInClose( data->inHandle );
Chris@1397 2169 errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
Chris@1397 2170 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2171 return;
Chris@1397 2172 }
Chris@559 2173 }
Chris@559 2174
Chris@559 2175 midiInClose( data->inHandle );
Chris@559 2176 connected_ = false;
Chris@1397 2177 LeaveCriticalSection( &(data->_mutex) );
Chris@559 2178 }
Chris@559 2179 }
Chris@559 2180
Chris@1397 2181 unsigned int MidiInWinMM :: getPortCount()
Chris@1397 2182 {
Chris@1397 2183 return midiInGetNumDevs();
Chris@1397 2184 }
Chris@1397 2185
Chris@1397 2186 std::string MidiInWinMM :: getPortName( unsigned int portNumber )
Chris@1397 2187 {
Chris@1397 2188 std::string stringName;
Chris@1397 2189 unsigned int nDevices = midiInGetNumDevs();
Chris@1397 2190 if ( portNumber >= nDevices ) {
Chris@1397 2191 std::ostringstream ost;
Chris@1397 2192 ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@1397 2193 errorString_ = ost.str();
Chris@1397 2194 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2195 return stringName;
Chris@1397 2196 }
Chris@1397 2197
Chris@1397 2198 MIDIINCAPS deviceCaps;
Chris@1397 2199 midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
Chris@1397 2200
Chris@1397 2201 #if defined( UNICODE ) || defined( _UNICODE )
Chris@1397 2202 int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;
Chris@1397 2203 stringName.assign( length, 0 );
Chris@1397 2204 length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);
Chris@1397 2205 #else
Chris@1397 2206 stringName = std::string( deviceCaps.szPname );
Chris@1397 2207 #endif
Chris@1397 2208
Chris@1397 2209 // Next lines added to add the portNumber to the name so that
Chris@1397 2210 // the device's names are sure to be listed with individual names
Chris@1397 2211 // even when they have the same brand name
Chris@1397 2212 std::ostringstream os;
Chris@1397 2213 os << " ";
Chris@1397 2214 os << portNumber;
Chris@1397 2215 stringName += os.str();
Chris@1397 2216
Chris@1397 2217 return stringName;
Chris@1397 2218 }
Chris@1397 2219
Chris@1397 2220 //*********************************************************************//
Chris@1397 2221 // API: Windows MM
Chris@1397 2222 // Class Definitions: MidiOutWinMM
Chris@1397 2223 //*********************************************************************//
Chris@1397 2224
Chris@1397 2225 MidiOutWinMM :: MidiOutWinMM( const std::string clientName ) : MidiOutApi()
Chris@1397 2226 {
Chris@1397 2227 initialize( clientName );
Chris@1397 2228 }
Chris@1397 2229
Chris@1397 2230 MidiOutWinMM :: ~MidiOutWinMM()
Chris@559 2231 {
Chris@559 2232 // Close a connection if it exists.
Chris@559 2233 closePort();
Chris@559 2234
Chris@559 2235 // Cleanup.
Chris@559 2236 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2237 delete data;
Chris@559 2238 }
Chris@559 2239
Chris@1397 2240 void MidiOutWinMM :: initialize( const std::string& /*clientName*/ )
Chris@559 2241 {
Chris@559 2242 // We'll issue a warning here if no devices are available but not
Chris@559 2243 // throw an error since the user can plug something in later.
Chris@559 2244 unsigned int nDevices = midiOutGetNumDevs();
Chris@559 2245 if ( nDevices == 0 ) {
Chris@1397 2246 errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available.";
Chris@1397 2247 error( RtMidiError::WARNING, errorString_ );
Chris@559 2248 }
Chris@559 2249
Chris@559 2250 // Save our api-specific connection information.
Chris@559 2251 WinMidiData *data = (WinMidiData *) new WinMidiData;
Chris@559 2252 apiData_ = (void *) data;
Chris@559 2253 }
Chris@559 2254
Chris@1397 2255 unsigned int MidiOutWinMM :: getPortCount()
Chris@1397 2256 {
Chris@1397 2257 return midiOutGetNumDevs();
Chris@1397 2258 }
Chris@1397 2259
Chris@1397 2260 std::string MidiOutWinMM :: getPortName( unsigned int portNumber )
Chris@1397 2261 {
Chris@1397 2262 std::string stringName;
Chris@1397 2263 unsigned int nDevices = midiOutGetNumDevs();
Chris@1397 2264 if ( portNumber >= nDevices ) {
Chris@1397 2265 std::ostringstream ost;
Chris@1397 2266 ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@1397 2267 errorString_ = ost.str();
Chris@1397 2268 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2269 return stringName;
Chris@1397 2270 }
Chris@1397 2271
Chris@1397 2272 MIDIOUTCAPS deviceCaps;
Chris@1397 2273 midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));
Chris@1397 2274
Chris@1397 2275 #if defined( UNICODE ) || defined( _UNICODE )
Chris@1397 2276 int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;
Chris@1397 2277 stringName.assign( length, 0 );
Chris@1397 2278 length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);
Chris@1397 2279 #else
Chris@1397 2280 stringName = std::string( deviceCaps.szPname );
Chris@1397 2281 #endif
Chris@1397 2282
Chris@1397 2283 // Next lines added to add the portNumber to the name so that
Chris@1397 2284 // the device's names are sure to be listed with individual names
Chris@1397 2285 // even when they have the same brand name
Chris@1397 2286 std::ostringstream os;
Chris@1397 2287 os << " ";
Chris@1397 2288 os << portNumber;
Chris@1397 2289 stringName += os.str();
Chris@1397 2290
Chris@1397 2291 return stringName;
Chris@1397 2292 }
Chris@1397 2293
Chris@1397 2294 void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@559 2295 {
Chris@559 2296 if ( connected_ ) {
Chris@1397 2297 errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!";
Chris@1397 2298 error( RtMidiError::WARNING, errorString_ );
Chris@559 2299 return;
Chris@559 2300 }
Chris@559 2301
Chris@559 2302 unsigned int nDevices = midiOutGetNumDevs();
Chris@559 2303 if (nDevices < 1) {
Chris@1397 2304 errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!";
Chris@1397 2305 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
Chris@1397 2306 return;
Chris@559 2307 }
Chris@559 2308
Chris@559 2309 if ( portNumber >= nDevices ) {
Chris@1397 2310 std::ostringstream ost;
Chris@1397 2311 ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 2312 errorString_ = ost.str();
Chris@1397 2313 error( RtMidiError::INVALID_PARAMETER, errorString_ );
Chris@1397 2314 return;
Chris@559 2315 }
Chris@559 2316
Chris@559 2317 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2318 MMRESULT result = midiOutOpen( &data->outHandle,
Chris@559 2319 portNumber,
Chris@559 2320 (DWORD)NULL,
Chris@559 2321 (DWORD)NULL,
Chris@559 2322 CALLBACK_NULL );
Chris@559 2323 if ( result != MMSYSERR_NOERROR ) {
Chris@1397 2324 errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port.";
Chris@1397 2325 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2326 return;
Chris@559 2327 }
Chris@559 2328
Chris@559 2329 connected_ = true;
Chris@559 2330 }
Chris@559 2331
Chris@1397 2332 void MidiOutWinMM :: closePort( void )
Chris@559 2333 {
Chris@559 2334 if ( connected_ ) {
Chris@559 2335 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2336 midiOutReset( data->outHandle );
Chris@559 2337 midiOutClose( data->outHandle );
Chris@559 2338 connected_ = false;
Chris@559 2339 }
Chris@559 2340 }
Chris@559 2341
Chris@1397 2342 void MidiOutWinMM :: openVirtualPort( std::string /*portName*/ )
Chris@559 2343 {
Chris@559 2344 // This function cannot be implemented for the Windows MM MIDI API.
Chris@1397 2345 errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
Chris@1397 2346 error( RtMidiError::WARNING, errorString_ );
Chris@559 2347 }
Chris@559 2348
Chris@1397 2349 void MidiOutWinMM :: sendMessage( std::vector<unsigned char> *message )
Chris@559 2350 {
Chris@1397 2351 if ( !connected_ ) return;
Chris@1397 2352
Chris@1397 2353 unsigned int nBytes = static_cast<unsigned int>(message->size());
Chris@559 2354 if ( nBytes == 0 ) {
Chris@1397 2355 errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!";
Chris@1397 2356 error( RtMidiError::WARNING, errorString_ );
Chris@559 2357 return;
Chris@559 2358 }
Chris@559 2359
Chris@559 2360 MMRESULT result;
Chris@559 2361 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2362 if ( message->at(0) == 0xF0 ) { // Sysex message
Chris@559 2363
Chris@559 2364 // Allocate buffer for sysex data.
Chris@559 2365 char *buffer = (char *) malloc( nBytes );
Chris@559 2366 if ( buffer == NULL ) {
Chris@1397 2367 errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!";
Chris@1397 2368 error( RtMidiError::MEMORY_ERROR, errorString_ );
Chris@1397 2369 return;
Chris@559 2370 }
Chris@559 2371
Chris@559 2372 // Copy data to buffer.
Chris@1397 2373 for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message->at(i);
Chris@559 2374
Chris@559 2375 // Create and prepare MIDIHDR structure.
Chris@559 2376 MIDIHDR sysex;
Chris@559 2377 sysex.lpData = (LPSTR) buffer;
Chris@559 2378 sysex.dwBufferLength = nBytes;
Chris@559 2379 sysex.dwFlags = 0;
Chris@559 2380 result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) );
Chris@559 2381 if ( result != MMSYSERR_NOERROR ) {
Chris@559 2382 free( buffer );
Chris@1397 2383 errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header.";
Chris@1397 2384 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2385 return;
Chris@559 2386 }
Chris@559 2387
Chris@559 2388 // Send the message.
Chris@559 2389 result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) );
Chris@559 2390 if ( result != MMSYSERR_NOERROR ) {
Chris@559 2391 free( buffer );
Chris@1397 2392 errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message.";
Chris@1397 2393 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2394 return;
Chris@559 2395 }
Chris@559 2396
Chris@559 2397 // Unprepare the buffer and MIDIHDR.
Chris@559 2398 while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 );
Chris@559 2399 free( buffer );
Chris@559 2400 }
Chris@559 2401 else { // Channel or system message.
Chris@559 2402
Chris@559 2403 // Make sure the message size isn't too big.
Chris@559 2404 if ( nBytes > 3 ) {
Chris@1397 2405 errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!";
Chris@1397 2406 error( RtMidiError::WARNING, errorString_ );
Chris@559 2407 return;
Chris@559 2408 }
Chris@559 2409
Chris@559 2410 // Pack MIDI bytes into double word.
Chris@559 2411 DWORD packet;
Chris@559 2412 unsigned char *ptr = (unsigned char *) &packet;
Chris@1397 2413 for ( unsigned int i=0; i<nBytes; ++i ) {
Chris@559 2414 *ptr = message->at(i);
Chris@1397 2415 ++ptr;
Chris@559 2416 }
Chris@559 2417
Chris@559 2418 // Send the message immediately.
Chris@559 2419 result = midiOutShortMsg( data->outHandle, packet );
Chris@559 2420 if ( result != MMSYSERR_NOERROR ) {
Chris@1397 2421 errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message.";
Chris@1397 2422 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@559 2423 }
Chris@559 2424 }
Chris@559 2425 }
Chris@559 2426
Chris@559 2427 #endif // __WINDOWS_MM__
Chris@609 2428
Chris@1397 2429
Chris@1397 2430 //*********************************************************************//
Chris@1397 2431 // API: UNIX JACK
Chris@1397 2432 //
Chris@1397 2433 // Written primarily by Alexander Svetalkin, with updates for delta
Chris@1397 2434 // time by Gary Scavone, April 2011.
Chris@1397 2435 //
Chris@1397 2436 // *********************************************************************//
Chris@1397 2437
Chris@1397 2438 #if defined(__UNIX_JACK__)
Chris@1397 2439
Chris@1397 2440 // JACK header files
Chris@1397 2441 #include <jack/jack.h>
Chris@1397 2442 #include <jack/midiport.h>
Chris@1397 2443 #include <jack/ringbuffer.h>
Chris@1397 2444
Chris@1397 2445 #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
Chris@1397 2446
Chris@1397 2447 struct JackMidiData {
Chris@1397 2448 jack_client_t *client;
Chris@1397 2449 jack_port_t *port;
Chris@1397 2450 jack_ringbuffer_t *buffSize;
Chris@1397 2451 jack_ringbuffer_t *buffMessage;
Chris@1397 2452 jack_time_t lastTime;
Chris@1397 2453 MidiInApi :: RtMidiInData *rtMidiIn;
Chris@1397 2454 };
Chris@1397 2455
Chris@1397 2456 //*********************************************************************//
Chris@1397 2457 // API: JACK
Chris@1397 2458 // Class Definitions: MidiInJack
Chris@1397 2459 //*********************************************************************//
Chris@1397 2460
Chris@1397 2461 static int jackProcessIn( jack_nframes_t nframes, void *arg )
Chris@609 2462 {
Chris@1397 2463 JackMidiData *jData = (JackMidiData *) arg;
Chris@1397 2464 MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn;
Chris@1397 2465 jack_midi_event_t event;
Chris@1397 2466 jack_time_t time;
Chris@1397 2467
Chris@1397 2468 // Is port created?
Chris@1397 2469 if ( jData->port == NULL ) return 0;
Chris@1397 2470 void *buff = jack_port_get_buffer( jData->port, nframes );
Chris@1397 2471
Chris@1397 2472 // We have midi events in buffer
Chris@1397 2473 int evCount = jack_midi_get_event_count( buff );
Chris@1397 2474 for (int j = 0; j < evCount; j++) {
Chris@1397 2475 MidiInApi::MidiMessage message;
Chris@1397 2476 message.bytes.clear();
Chris@1397 2477
Chris@1397 2478 jack_midi_event_get( &event, buff, j );
Chris@1397 2479
Chris@1397 2480 for ( unsigned int i = 0; i < event.size; i++ )
Chris@1397 2481 message.bytes.push_back( event.buffer[i] );
Chris@1397 2482
Chris@1397 2483 // Compute the delta time.
Chris@1397 2484 time = jack_get_time();
Chris@1397 2485 if ( rtData->firstMessage == true )
Chris@1397 2486 rtData->firstMessage = false;
Chris@1397 2487 else
Chris@1397 2488 message.timeStamp = ( time - jData->lastTime ) * 0.000001;
Chris@1397 2489
Chris@1397 2490 jData->lastTime = time;
Chris@1397 2491
Chris@1397 2492 if ( !rtData->continueSysex ) {
Chris@1397 2493 if ( rtData->usingCallback ) {
Chris@1397 2494 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;
Chris@1397 2495 callback( message.timeStamp, &message.bytes, rtData->userData );
Chris@1397 2496 }
Chris@1397 2497 else {
Chris@1397 2498 // As long as we haven't reached our queue size limit, push the message.
Chris@1397 2499 if ( rtData->queue.size < rtData->queue.ringSize ) {
Chris@1397 2500 rtData->queue.ring[rtData->queue.back++] = message;
Chris@1397 2501 if ( rtData->queue.back == rtData->queue.ringSize )
Chris@1397 2502 rtData->queue.back = 0;
Chris@1397 2503 rtData->queue.size++;
Chris@1397 2504 }
Chris@1397 2505 else
Chris@1397 2506 std::cerr << "\nMidiInJack: message queue limit reached!!\n\n";
Chris@1397 2507 }
Chris@1397 2508 }
Chris@1397 2509 }
Chris@1397 2510
Chris@1397 2511 return 0;
Chris@609 2512 }
Chris@609 2513
Chris@1397 2514 MidiInJack :: MidiInJack( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
Chris@609 2515 {
Chris@1397 2516 initialize( clientName );
Chris@609 2517 }
Chris@609 2518
Chris@1397 2519 void MidiInJack :: initialize( const std::string& clientName )
Chris@609 2520 {
Chris@1397 2521 JackMidiData *data = new JackMidiData;
Chris@1397 2522 apiData_ = (void *) data;
Chris@1397 2523
Chris@1397 2524 data->rtMidiIn = &inputData_;
Chris@1397 2525 data->port = NULL;
Chris@1397 2526 data->client = NULL;
Chris@1397 2527 this->clientName = clientName;
Chris@1397 2528
Chris@1397 2529 connect();
Chris@609 2530 }
Chris@609 2531
Chris@1397 2532 void MidiInJack :: connect()
Chris@609 2533 {
Chris@1397 2534 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2535 if ( data->client )
Chris@1397 2536 return;
Chris@1397 2537
Chris@1397 2538 // Initialize JACK client
Chris@1397 2539 if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
Chris@1397 2540 errorString_ = "MidiInJack::initialize: JACK server not running?";
Chris@1397 2541 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2542 return;
Chris@1397 2543 }
Chris@1397 2544
Chris@1397 2545 jack_set_process_callback( data->client, jackProcessIn, data );
Chris@1397 2546 jack_activate( data->client );
Chris@609 2547 }
Chris@609 2548
Chris@1397 2549 MidiInJack :: ~MidiInJack()
Chris@609 2550 {
Chris@1397 2551 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2552 closePort();
Chris@1397 2553
Chris@1397 2554 if ( data->client )
Chris@1397 2555 jack_client_close( data->client );
Chris@1397 2556 delete data;
Chris@609 2557 }
Chris@609 2558
Chris@1397 2559 void MidiInJack :: openPort( unsigned int portNumber, const std::string portName )
Chris@609 2560 {
Chris@1397 2561 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2562
Chris@1397 2563 connect();
Chris@1397 2564
Chris@1397 2565 // Creating new port
Chris@1397 2566 if ( data->port == NULL)
Chris@1397 2567 data->port = jack_port_register( data->client, portName.c_str(),
Chris@1397 2568 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
Chris@1397 2569
Chris@1397 2570 if ( data->port == NULL) {
Chris@1397 2571 errorString_ = "MidiInJack::openPort: JACK error creating port";
Chris@1397 2572 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2573 return;
Chris@1397 2574 }
Chris@1397 2575
Chris@1397 2576 // Connecting to the output
Chris@1397 2577 std::string name = getPortName( portNumber );
Chris@1397 2578 jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );
Chris@1397 2579 }
Chris@1397 2580
Chris@1397 2581 void MidiInJack :: openVirtualPort( const std::string portName )
Chris@1397 2582 {
Chris@1397 2583 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2584
Chris@1397 2585 connect();
Chris@1397 2586 if ( data->port == NULL )
Chris@1397 2587 data->port = jack_port_register( data->client, portName.c_str(),
Chris@1397 2588 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
Chris@1397 2589
Chris@1397 2590 if ( data->port == NULL ) {
Chris@1397 2591 errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";
Chris@1397 2592 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2593 }
Chris@1397 2594 }
Chris@1397 2595
Chris@1397 2596 unsigned int MidiInJack :: getPortCount()
Chris@1397 2597 {
Chris@1397 2598 int count = 0;
Chris@1397 2599 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2600 connect();
Chris@1397 2601 if ( !data->client )
Chris@609 2602 return 0;
Chris@1397 2603
Chris@1397 2604 // List of available ports
Chris@1397 2605 const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
Chris@1397 2606
Chris@1397 2607 if ( ports == NULL ) return 0;
Chris@1397 2608 while ( ports[count] != NULL )
Chris@1397 2609 count++;
Chris@1397 2610
Chris@1397 2611 free( ports );
Chris@1397 2612
Chris@1397 2613 return count;
Chris@609 2614 }
Chris@609 2615
Chris@1397 2616 std::string MidiInJack :: getPortName( unsigned int portNumber )
Chris@609 2617 {
Chris@1397 2618 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2619 std::string retStr("");
Chris@1397 2620
Chris@1397 2621 connect();
Chris@1397 2622
Chris@1397 2623 // List of available ports
Chris@1397 2624 const char **ports = jack_get_ports( data->client, NULL,
Chris@1397 2625 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
Chris@1397 2626
Chris@1397 2627 // Check port validity
Chris@1397 2628 if ( ports == NULL ) {
Chris@1397 2629 errorString_ = "MidiInJack::getPortName: no ports available!";
Chris@1397 2630 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2631 return retStr;
Chris@1397 2632 }
Chris@1397 2633
Chris@1397 2634 if ( ports[portNumber] == NULL ) {
Chris@1397 2635 std::ostringstream ost;
Chris@1397 2636 ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@1397 2637 errorString_ = ost.str();
Chris@1397 2638 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2639 }
Chris@1397 2640 else retStr.assign( ports[portNumber] );
Chris@1397 2641
Chris@1397 2642 free( ports );
Chris@1397 2643 return retStr;
Chris@609 2644 }
Chris@609 2645
Chris@1397 2646 void MidiInJack :: closePort()
Chris@609 2647 {
Chris@1397 2648 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2649
Chris@1397 2650 if ( data->port == NULL ) return;
Chris@1397 2651 jack_port_unregister( data->client, data->port );
Chris@1397 2652 data->port = NULL;
Chris@1397 2653 }
Chris@1397 2654
Chris@1397 2655 //*********************************************************************//
Chris@1397 2656 // API: JACK
Chris@1397 2657 // Class Definitions: MidiOutJack
Chris@1397 2658 //*********************************************************************//
Chris@1397 2659
Chris@1397 2660 // Jack process callback
Chris@1397 2661 static int jackProcessOut( jack_nframes_t nframes, void *arg )
Chris@1397 2662 {
Chris@1397 2663 JackMidiData *data = (JackMidiData *) arg;
Chris@1397 2664 jack_midi_data_t *midiData;
Chris@1397 2665 int space;
Chris@1397 2666
Chris@1397 2667 // Is port created?
Chris@1397 2668 if ( data->port == NULL ) return 0;
Chris@1397 2669
Chris@1397 2670 void *buff = jack_port_get_buffer( data->port, nframes );
Chris@1397 2671 jack_midi_clear_buffer( buff );
Chris@1397 2672
Chris@1397 2673 while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
Chris@1397 2674 jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) );
Chris@1397 2675 midiData = jack_midi_event_reserve( buff, 0, space );
Chris@1397 2676
Chris@1397 2677 jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );
Chris@1397 2678 }
Chris@1397 2679
Chris@1397 2680 return 0;
Chris@1397 2681 }
Chris@1397 2682
Chris@1397 2683 MidiOutJack :: MidiOutJack( const std::string clientName ) : MidiOutApi()
Chris@1397 2684 {
Chris@1397 2685 initialize( clientName );
Chris@1397 2686 }
Chris@1397 2687
Chris@1397 2688 void MidiOutJack :: initialize( const std::string& clientName )
Chris@1397 2689 {
Chris@1397 2690 JackMidiData *data = new JackMidiData;
Chris@1397 2691 apiData_ = (void *) data;
Chris@1397 2692
Chris@1397 2693 data->port = NULL;
Chris@1397 2694 data->client = NULL;
Chris@1397 2695 this->clientName = clientName;
Chris@1397 2696
Chris@1397 2697 connect();
Chris@1397 2698 }
Chris@1397 2699
Chris@1397 2700 void MidiOutJack :: connect()
Chris@1397 2701 {
Chris@1397 2702 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2703 if ( data->client )
Chris@1397 2704 return;
Chris@1397 2705
Chris@1397 2706 // Initialize output ringbuffers
Chris@1397 2707 data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
Chris@1397 2708 data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
Chris@1397 2709
Chris@1397 2710 // Initialize JACK client
Chris@1397 2711 if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
Chris@1397 2712 errorString_ = "MidiOutJack::initialize: JACK server not running?";
Chris@1397 2713 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2714 return;
Chris@1397 2715 }
Chris@1397 2716
Chris@1397 2717 jack_set_process_callback( data->client, jackProcessOut, data );
Chris@1397 2718 jack_activate( data->client );
Chris@1397 2719 }
Chris@1397 2720
Chris@1397 2721 MidiOutJack :: ~MidiOutJack()
Chris@1397 2722 {
Chris@1397 2723 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2724 closePort();
Chris@1397 2725
Chris@1397 2726 // Cleanup
Chris@1397 2727 jack_ringbuffer_free( data->buffSize );
Chris@1397 2728 jack_ringbuffer_free( data->buffMessage );
Chris@1397 2729 if ( data->client ) {
Chris@1397 2730 jack_client_close( data->client );
Chris@1397 2731 }
Chris@1397 2732
Chris@1397 2733 delete data;
Chris@1397 2734 }
Chris@1397 2735
Chris@1397 2736 void MidiOutJack :: openPort( unsigned int portNumber, const std::string portName )
Chris@1397 2737 {
Chris@1397 2738 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2739
Chris@1397 2740 connect();
Chris@1397 2741
Chris@1397 2742 // Creating new port
Chris@1397 2743 if ( data->port == NULL )
Chris@1397 2744 data->port = jack_port_register( data->client, portName.c_str(),
Chris@1397 2745 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
Chris@1397 2746
Chris@1397 2747 if ( data->port == NULL ) {
Chris@1397 2748 errorString_ = "MidiOutJack::openPort: JACK error creating port";
Chris@1397 2749 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2750 return;
Chris@1397 2751 }
Chris@1397 2752
Chris@1397 2753 // Connecting to the output
Chris@1397 2754 std::string name = getPortName( portNumber );
Chris@1397 2755 jack_connect( data->client, jack_port_name( data->port ), name.c_str() );
Chris@1397 2756 }
Chris@1397 2757
Chris@1397 2758 void MidiOutJack :: openVirtualPort( const std::string portName )
Chris@1397 2759 {
Chris@1397 2760 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2761
Chris@1397 2762 connect();
Chris@1397 2763 if ( data->port == NULL )
Chris@1397 2764 data->port = jack_port_register( data->client, portName.c_str(),
Chris@1397 2765 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
Chris@1397 2766
Chris@1397 2767 if ( data->port == NULL ) {
Chris@1397 2768 errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";
Chris@1397 2769 error( RtMidiError::DRIVER_ERROR, errorString_ );
Chris@1397 2770 }
Chris@1397 2771 }
Chris@1397 2772
Chris@1397 2773 unsigned int MidiOutJack :: getPortCount()
Chris@1397 2774 {
Chris@1397 2775 int count = 0;
Chris@1397 2776 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2777 connect();
Chris@1397 2778 if ( !data->client )
Chris@609 2779 return 0;
Chris@1397 2780
Chris@1397 2781 // List of available ports
Chris@1397 2782 const char **ports = jack_get_ports( data->client, NULL,
Chris@1397 2783 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
Chris@1397 2784
Chris@1397 2785 if ( ports == NULL ) return 0;
Chris@1397 2786 while ( ports[count] != NULL )
Chris@1397 2787 count++;
Chris@1397 2788
Chris@1397 2789 free( ports );
Chris@1397 2790
Chris@1397 2791 return count;
Chris@609 2792 }
Chris@609 2793
Chris@1397 2794 std::string MidiOutJack :: getPortName( unsigned int portNumber )
Chris@609 2795 {
Chris@1397 2796 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2797 std::string retStr("");
Chris@1397 2798
Chris@1397 2799 connect();
Chris@1397 2800
Chris@1397 2801 // List of available ports
Chris@1397 2802 const char **ports = jack_get_ports( data->client, NULL,
Chris@1397 2803 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
Chris@1397 2804
Chris@1397 2805 // Check port validity
Chris@1397 2806 if ( ports == NULL) {
Chris@1397 2807 errorString_ = "MidiOutJack::getPortName: no ports available!";
Chris@1397 2808 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2809 return retStr;
Chris@1397 2810 }
Chris@1397 2811
Chris@1397 2812 if ( ports[portNumber] == NULL) {
Chris@1397 2813 std::ostringstream ost;
Chris@1397 2814 ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@1397 2815 errorString_ = ost.str();
Chris@1397 2816 error( RtMidiError::WARNING, errorString_ );
Chris@1397 2817 }
Chris@1397 2818 else retStr.assign( ports[portNumber] );
Chris@1397 2819
Chris@1397 2820 free( ports );
Chris@1397 2821 return retStr;
Chris@609 2822 }
Chris@609 2823
Chris@1397 2824 void MidiOutJack :: closePort()
Chris@609 2825 {
Chris@1397 2826 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2827
Chris@1397 2828 if ( data->port == NULL ) return;
Chris@1397 2829 jack_port_unregister( data->client, data->port );
Chris@1397 2830 data->port = NULL;
Chris@609 2831 }
Chris@609 2832
Chris@1397 2833 void MidiOutJack :: sendMessage( std::vector<unsigned char> *message )
Chris@609 2834 {
Chris@1397 2835 int nBytes = message->size();
Chris@1397 2836 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
Chris@1397 2837
Chris@1397 2838 // Write full message to buffer
Chris@1397 2839 jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0],
Chris@1397 2840 message->size() );
Chris@1397 2841 jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
Chris@609 2842 }
Chris@609 2843
Chris@1397 2844 #endif // __UNIX_JACK__