annotate data/midi/rtmidi/RtMidi.cpp @ 706:579b2da21e7a

Make FileSource capable of handling resource files. Without this, we failed to open the silent resource file used as a placeholder in templates and thus failed to replace it with the proper file after loading the template -- the consequence was that (although the right audio file ended up being shown as the main model) any derived models were not regenerated
author Chris Cannam
date Fri, 07 Oct 2011 17:04:09 +0100
parents 1424aa29ae95
children e802e550a1f2
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@565 11 Copyright (c) 2003-2009 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@559 25 requested to send the modifications to the original developer so that
Chris@559 26 they can be incorporated into the canonical version.
Chris@559 27
Chris@559 28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@559 29 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@559 30 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
Chris@559 31 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
Chris@559 32 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@559 33 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@559 34 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@559 35 */
Chris@559 36 /**********************************************************************/
Chris@559 37
Chris@565 38 // RtMidi: Version 1.0.8
Chris@559 39
Chris@559 40 #include "RtMidi.h"
Chris@559 41 #include <sstream>
Chris@559 42
Chris@559 43 //*********************************************************************//
Chris@559 44 // Common RtMidi Definitions
Chris@559 45 //*********************************************************************//
Chris@559 46
Chris@559 47 RtMidi :: RtMidi()
Chris@559 48 : apiData_( 0 ), connected_( false )
Chris@559 49 {
Chris@559 50 }
Chris@559 51
Chris@559 52 void RtMidi :: error( RtError::Type type )
Chris@559 53 {
Chris@559 54 if (type == RtError::WARNING) {
Chris@559 55 std::cerr << '\n' << errorString_ << "\n\n";
Chris@559 56 }
Chris@559 57 else if (type == RtError::DEBUG_WARNING) {
Chris@559 58 #if defined(__RTMIDI_DEBUG__)
Chris@559 59 std::cerr << '\n' << errorString_ << "\n\n";
Chris@559 60 #endif
Chris@559 61 }
Chris@559 62 else {
Chris@559 63 std::cerr << '\n' << errorString_ << "\n\n";
Chris@559 64 throw RtError( errorString_, type );
Chris@559 65 }
Chris@559 66 }
Chris@559 67
Chris@559 68 //*********************************************************************//
Chris@559 69 // Common RtMidiIn Definitions
Chris@559 70 //*********************************************************************//
Chris@559 71
Chris@565 72 RtMidiIn :: RtMidiIn( const std::string clientName ) : RtMidi()
Chris@559 73 {
Chris@565 74 this->initialize( clientName );
Chris@559 75 }
Chris@559 76
Chris@559 77 void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData )
Chris@559 78 {
Chris@559 79 if ( inputData_.usingCallback ) {
Chris@559 80 errorString_ = "RtMidiIn::setCallback: a callback function is already set!";
Chris@559 81 error( RtError::WARNING );
Chris@559 82 return;
Chris@559 83 }
Chris@559 84
Chris@559 85 if ( !callback ) {
Chris@559 86 errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";
Chris@559 87 error( RtError::WARNING );
Chris@559 88 return;
Chris@559 89 }
Chris@559 90
Chris@559 91 inputData_.userCallback = (void *) callback;
Chris@559 92 inputData_.userData = userData;
Chris@559 93 inputData_.usingCallback = true;
Chris@559 94 }
Chris@559 95
Chris@559 96 void RtMidiIn :: cancelCallback()
Chris@559 97 {
Chris@559 98 if ( !inputData_.usingCallback ) {
Chris@559 99 errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";
Chris@559 100 error( RtError::WARNING );
Chris@559 101 return;
Chris@559 102 }
Chris@559 103
Chris@559 104 inputData_.userCallback = 0;
Chris@559 105 inputData_.userData = 0;
Chris@559 106 inputData_.usingCallback = false;
Chris@559 107 }
Chris@559 108
Chris@559 109 void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize )
Chris@559 110 {
Chris@559 111 inputData_.queueLimit = queueSize;
Chris@559 112 }
Chris@559 113
Chris@559 114 void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )
Chris@559 115 {
Chris@559 116 inputData_.ignoreFlags = 0;
Chris@559 117 if ( midiSysex ) inputData_.ignoreFlags = 0x01;
Chris@559 118 if ( midiTime ) inputData_.ignoreFlags |= 0x02;
Chris@559 119 if ( midiSense ) inputData_.ignoreFlags |= 0x04;
Chris@559 120 }
Chris@559 121
Chris@559 122 double RtMidiIn :: getMessage( std::vector<unsigned char> *message )
Chris@559 123 {
Chris@559 124 message->clear();
Chris@559 125
Chris@559 126 if ( inputData_.usingCallback ) {
Chris@559 127 errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";
Chris@559 128 error( RtError::WARNING );
Chris@559 129 return 0.0;
Chris@559 130 }
Chris@559 131
Chris@559 132 if ( inputData_.queue.size() == 0 ) return 0.0;
Chris@559 133
Chris@559 134 // Copy queued message to the vector pointer argument and then "pop" it.
Chris@559 135 std::vector<unsigned char> *bytes = &(inputData_.queue.front().bytes);
Chris@559 136 message->assign( bytes->begin(), bytes->end() );
Chris@559 137 double deltaTime = inputData_.queue.front().timeStamp;
Chris@559 138 inputData_.queue.pop();
Chris@559 139
Chris@559 140 return deltaTime;
Chris@559 141 }
Chris@559 142
Chris@559 143 //*********************************************************************//
Chris@559 144 // Common RtMidiOut Definitions
Chris@559 145 //*********************************************************************//
Chris@559 146
Chris@565 147 RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi()
Chris@559 148 {
Chris@565 149 this->initialize( clientName );
Chris@559 150 }
Chris@559 151
Chris@559 152
Chris@559 153 //*********************************************************************//
Chris@559 154 // API: Macintosh OS-X
Chris@559 155 //*********************************************************************//
Chris@559 156
Chris@559 157 // API information found at:
Chris@559 158 // - http://developer. apple .com/audio/pdf/coreaudio.pdf
Chris@559 159
Chris@559 160 #if defined(__MACOSX_CORE__)
Chris@559 161
Chris@559 162 // The CoreMIDI API is based on the use of a callback function for
Chris@559 163 // MIDI input. We convert the system specific time stamps to delta
Chris@559 164 // time values.
Chris@559 165
Chris@559 166 // OS-X CoreMIDI header files.
Chris@559 167 #include <CoreMIDI/CoreMIDI.h>
Chris@559 168 #include <CoreAudio/HostTime.h>
Chris@559 169
Chris@559 170 // A structure to hold variables related to the CoreMIDI API
Chris@559 171 // implementation.
Chris@559 172 struct CoreMidiData {
Chris@559 173 MIDIClientRef client;
Chris@559 174 MIDIPortRef port;
Chris@559 175 MIDIEndpointRef endpoint;
Chris@559 176 MIDIEndpointRef destinationId;
Chris@559 177 unsigned long long lastTime;
Chris@559 178 };
Chris@559 179
Chris@559 180 //*********************************************************************//
Chris@559 181 // API: OS-X
Chris@559 182 // Class Definitions: RtMidiIn
Chris@559 183 //*********************************************************************//
Chris@559 184
Chris@559 185 void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef )
Chris@559 186 {
Chris@559 187 RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (procRef);
Chris@559 188 CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
Chris@559 189
Chris@559 190 unsigned char status;
Chris@559 191 unsigned short nBytes, iByte, size;
Chris@559 192 unsigned long long time;
Chris@565 193
Chris@565 194 bool& continueSysex = data->continueSysex;
Chris@565 195 RtMidiIn::MidiMessage& message = data->message;
Chris@565 196
Chris@559 197 const MIDIPacket *packet = &list->packet[0];
Chris@559 198 for ( unsigned int i=0; i<list->numPackets; ++i ) {
Chris@559 199
Chris@559 200 // My interpretation of the CoreMIDI documentation: all message
Chris@559 201 // types, except sysex, are complete within a packet and there may
Chris@559 202 // be several of them in a single packet. Sysex messages can be
Chris@565 203 // broken across multiple packets and PacketLists but are bundled
Chris@565 204 // alone within each packet (these packets do not contain other
Chris@565 205 // message types). If sysex messages are split across multiple
Chris@565 206 // MIDIPacketLists, they must be handled by multiple calls to this
Chris@565 207 // function.
Chris@559 208
Chris@559 209 nBytes = packet->length;
Chris@559 210 if ( nBytes == 0 ) continue;
Chris@559 211
Chris@559 212 // Calculate time stamp.
Chris@559 213 message.timeStamp = 0.0;
Chris@559 214 if ( data->firstMessage )
Chris@559 215 data->firstMessage = false;
Chris@559 216 else {
Chris@559 217 time = packet->timeStamp;
Chris@559 218 time -= apiData->lastTime;
Chris@559 219 time = AudioConvertHostTimeToNanos( time );
Chris@559 220 message.timeStamp = time * 0.000000001;
Chris@559 221 }
Chris@559 222 apiData->lastTime = packet->timeStamp;
Chris@559 223
Chris@559 224 iByte = 0;
Chris@559 225 if ( continueSysex ) {
Chris@559 226 // We have a continuing, segmented sysex message.
Chris@565 227 if ( !( data->ignoreFlags & 0x01 ) ) {
Chris@559 228 // If we're not ignoring sysex messages, copy the entire packet.
Chris@559 229 for ( unsigned int j=0; j<nBytes; j++ )
Chris@559 230 message.bytes.push_back( packet->data[j] );
Chris@559 231 }
Chris@565 232 continueSysex = packet->data[nBytes-1] != 0xF7;
Chris@565 233
Chris@559 234 if ( !continueSysex ) {
Chris@559 235 // If not a continuing sysex message, invoke the user callback function or queue the message.
Chris@559 236 if ( data->usingCallback && message.bytes.size() > 0 ) {
Chris@559 237 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 238 callback( message.timeStamp, &message.bytes, data->userData );
Chris@559 239 }
Chris@559 240 else {
Chris@559 241 // As long as we haven't reached our queue size limit, push the message.
Chris@559 242 if ( data->queueLimit > data->queue.size() )
Chris@559 243 data->queue.push( message );
Chris@559 244 else
Chris@559 245 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
Chris@559 246 }
Chris@559 247 message.bytes.clear();
Chris@559 248 }
Chris@559 249 }
Chris@559 250 else {
Chris@559 251 while ( iByte < nBytes ) {
Chris@559 252 size = 0;
Chris@559 253 // We are expecting that the next byte in the packet is a status byte.
Chris@559 254 status = packet->data[iByte];
Chris@559 255 if ( !(status & 0x80) ) break;
Chris@559 256 // Determine the number of bytes in the MIDI message.
Chris@559 257 if ( status < 0xC0 ) size = 3;
Chris@559 258 else if ( status < 0xE0 ) size = 2;
Chris@559 259 else if ( status < 0xF0 ) size = 3;
Chris@559 260 else if ( status == 0xF0 ) {
Chris@559 261 // A MIDI sysex
Chris@559 262 if ( data->ignoreFlags & 0x01 ) {
Chris@559 263 size = 0;
Chris@559 264 iByte = nBytes;
Chris@559 265 }
Chris@559 266 else size = nBytes - iByte;
Chris@565 267 continueSysex = packet->data[nBytes-1] != 0xF7;
Chris@559 268 }
Chris@559 269 else if ( status < 0xF3 ) {
Chris@559 270 if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) {
Chris@559 271 // A MIDI time code message and we're ignoring it.
Chris@559 272 size = 0;
Chris@559 273 iByte += 3;
Chris@559 274 }
Chris@559 275 else size = 3;
Chris@559 276 }
Chris@559 277 else if ( status == 0xF3 ) size = 2;
Chris@559 278 else if ( status == 0xF8 ) {
Chris@559 279 size = 1;
Chris@559 280 if ( data->ignoreFlags & 0x02 ) {
Chris@559 281 // A MIDI timing tick message and we're ignoring it.
Chris@559 282 size = 0;
Chris@559 283 iByte += 3;
Chris@559 284 }
Chris@559 285 }
Chris@559 286 else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
Chris@559 287 // A MIDI active sensing message and we're ignoring it.
Chris@559 288 size = 0;
Chris@559 289 iByte += 1;
Chris@559 290 }
Chris@559 291 else size = 1;
Chris@559 292
Chris@559 293 // Copy the MIDI data to our vector.
Chris@559 294 if ( size ) {
Chris@559 295 message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );
Chris@559 296 if ( !continueSysex ) {
Chris@559 297 // If not a continuing sysex message, invoke the user callback function or queue the message.
Chris@559 298 if ( data->usingCallback ) {
Chris@559 299 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 300 callback( message.timeStamp, &message.bytes, data->userData );
Chris@559 301 }
Chris@559 302 else {
Chris@559 303 // As long as we haven't reached our queue size limit, push the message.
Chris@559 304 if ( data->queueLimit > data->queue.size() )
Chris@559 305 data->queue.push( message );
Chris@559 306 else
Chris@559 307 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
Chris@559 308 }
Chris@559 309 message.bytes.clear();
Chris@559 310 }
Chris@559 311 iByte += size;
Chris@559 312 }
Chris@559 313 }
Chris@559 314 }
Chris@559 315 packet = MIDIPacketNext(packet);
Chris@559 316 }
Chris@559 317 }
Chris@559 318
Chris@565 319 void RtMidiIn :: initialize( const std::string& clientName )
Chris@559 320 {
Chris@559 321 // Set up our client.
Chris@559 322 MIDIClientRef client;
Chris@565 323 OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
Chris@559 324 if ( result != noErr ) {
Chris@559 325 errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object.";
Chris@559 326 error( RtError::DRIVER_ERROR );
Chris@559 327 }
Chris@559 328
Chris@559 329 // Save our api-specific connection information.
Chris@559 330 CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
Chris@559 331 data->client = client;
Chris@559 332 data->endpoint = 0;
Chris@559 333 apiData_ = (void *) data;
Chris@559 334 inputData_.apiData = (void *) data;
Chris@559 335 }
Chris@559 336
Chris@565 337 void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
Chris@559 338 {
Chris@559 339 if ( connected_ ) {
Chris@559 340 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
Chris@559 341 error( RtError::WARNING );
Chris@559 342 return;
Chris@559 343 }
Chris@559 344
Chris@559 345 unsigned int nSrc = MIDIGetNumberOfSources();
Chris@559 346 if (nSrc < 1) {
Chris@559 347 errorString_ = "RtMidiIn::openPort: no MIDI input sources found!";
Chris@559 348 error( RtError::NO_DEVICES_FOUND );
Chris@559 349 }
Chris@559 350
Chris@559 351 std::ostringstream ost;
Chris@559 352 if ( portNumber >= nSrc ) {
Chris@559 353 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 354 errorString_ = ost.str();
Chris@559 355 error( RtError::INVALID_PARAMETER );
Chris@559 356 }
Chris@559 357
Chris@559 358 MIDIPortRef port;
Chris@559 359 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@565 360 OSStatus result = MIDIInputPortCreate( data->client,
Chris@565 361 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
Chris@565 362 midiInputCallback, (void *)&inputData_, &port );
Chris@559 363 if ( result != noErr ) {
Chris@559 364 MIDIClientDispose( data->client );
Chris@559 365 errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port.";
Chris@559 366 error( RtError::DRIVER_ERROR );
Chris@559 367 }
Chris@559 368
Chris@559 369 // Get the desired input source identifier.
Chris@559 370 MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
Chris@559 371 if ( endpoint == NULL ) {
Chris@559 372 MIDIPortDispose( port );
Chris@559 373 MIDIClientDispose( data->client );
Chris@559 374 errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference.";
Chris@559 375 error( RtError::DRIVER_ERROR );
Chris@559 376 }
Chris@559 377
Chris@559 378 // Make the connection.
Chris@559 379 result = MIDIPortConnectSource( port, endpoint, NULL );
Chris@559 380 if ( result != noErr ) {
Chris@559 381 MIDIPortDispose( port );
Chris@559 382 MIDIClientDispose( data->client );
Chris@559 383 errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port.";
Chris@559 384 error( RtError::DRIVER_ERROR );
Chris@559 385 }
Chris@559 386
Chris@559 387 // Save our api-specific port information.
Chris@559 388 data->port = port;
Chris@559 389
Chris@559 390 connected_ = true;
Chris@559 391 }
Chris@559 392
Chris@559 393 void RtMidiIn :: openVirtualPort( const std::string portName )
Chris@559 394 {
Chris@559 395 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@559 396
Chris@559 397 // Create a virtual MIDI input destination.
Chris@559 398 MIDIEndpointRef endpoint;
Chris@559 399 OSStatus result = MIDIDestinationCreate( data->client,
Chris@559 400 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
Chris@559 401 midiInputCallback, (void *)&inputData_, &endpoint );
Chris@559 402 if ( result != noErr ) {
Chris@559 403 errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination.";
Chris@559 404 error( RtError::DRIVER_ERROR );
Chris@559 405 }
Chris@559 406
Chris@559 407 // Save our api-specific connection information.
Chris@559 408 data->endpoint = endpoint;
Chris@559 409 }
Chris@559 410
Chris@559 411 void RtMidiIn :: closePort( void )
Chris@559 412 {
Chris@559 413 if ( connected_ ) {
Chris@559 414 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@559 415 MIDIPortDispose( data->port );
Chris@559 416 connected_ = false;
Chris@559 417 }
Chris@559 418 }
Chris@559 419
Chris@559 420 RtMidiIn :: ~RtMidiIn()
Chris@559 421 {
Chris@559 422 // Close a connection if it exists.
Chris@559 423 closePort();
Chris@559 424
Chris@559 425 // Cleanup.
Chris@559 426 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@559 427 MIDIClientDispose( data->client );
Chris@559 428 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
Chris@559 429 delete data;
Chris@559 430 }
Chris@559 431
Chris@559 432 unsigned int RtMidiIn :: getPortCount()
Chris@559 433 {
Chris@559 434 return MIDIGetNumberOfSources();
Chris@559 435 }
Chris@559 436
Chris@559 437 std::string RtMidiIn :: getPortName( unsigned int portNumber )
Chris@559 438 {
Chris@559 439 CFStringRef nameRef;
Chris@559 440 MIDIEndpointRef portRef;
Chris@559 441 std::ostringstream ost;
Chris@559 442 char name[128];
Chris@559 443
Chris@559 444 if ( portNumber >= MIDIGetNumberOfSources() ) {
Chris@559 445 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 446 errorString_ = ost.str();
Chris@559 447 error( RtError::INVALID_PARAMETER );
Chris@559 448 }
Chris@559 449 portRef = MIDIGetSource( portNumber );
Chris@559 450
Chris@559 451 MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
Chris@559 452 CFStringGetCString( nameRef, name, sizeof(name), 0);
Chris@559 453 CFRelease( nameRef );
Chris@559 454 std::string stringName = name;
Chris@559 455 return stringName;
Chris@559 456 }
Chris@559 457
Chris@559 458 //*********************************************************************//
Chris@559 459 // API: OS-X
Chris@559 460 // Class Definitions: RtMidiOut
Chris@559 461 //*********************************************************************//
Chris@559 462
Chris@559 463 unsigned int RtMidiOut :: getPortCount()
Chris@559 464 {
Chris@559 465 return MIDIGetNumberOfDestinations();
Chris@559 466 }
Chris@559 467
Chris@559 468 std::string RtMidiOut :: getPortName( unsigned int portNumber )
Chris@559 469 {
Chris@559 470 CFStringRef nameRef;
Chris@559 471 MIDIEndpointRef portRef;
Chris@559 472 std::ostringstream ost;
Chris@559 473 char name[128];
Chris@559 474
Chris@559 475 if ( portNumber >= MIDIGetNumberOfDestinations() ) {
Chris@559 476 ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 477 errorString_ = ost.str();
Chris@559 478 error( RtError::INVALID_PARAMETER );
Chris@559 479 }
Chris@559 480 portRef = MIDIGetDestination( portNumber );
Chris@559 481
Chris@559 482 MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef );
Chris@559 483 CFStringGetCString( nameRef, name, sizeof(name), 0);
Chris@559 484 CFRelease( nameRef );
Chris@559 485 std::string stringName = name;
Chris@559 486 return stringName;
Chris@559 487 }
Chris@559 488
Chris@565 489 void RtMidiOut :: initialize( const std::string& clientName )
Chris@559 490 {
Chris@559 491 // Set up our client.
Chris@559 492 MIDIClientRef client;
Chris@565 493 OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client );
Chris@559 494 if ( result != noErr ) {
Chris@559 495 errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object.";
Chris@559 496 error( RtError::DRIVER_ERROR );
Chris@559 497 }
Chris@559 498
Chris@559 499 // Save our api-specific connection information.
Chris@559 500 CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
Chris@559 501 data->client = client;
Chris@559 502 data->endpoint = 0;
Chris@559 503 apiData_ = (void *) data;
Chris@559 504 }
Chris@559 505
Chris@565 506 void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
Chris@559 507 {
Chris@559 508 if ( connected_ ) {
Chris@559 509 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
Chris@559 510 error( RtError::WARNING );
Chris@559 511 return;
Chris@559 512 }
Chris@559 513
Chris@559 514 unsigned int nDest = MIDIGetNumberOfDestinations();
Chris@559 515 if (nDest < 1) {
Chris@559 516 errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!";
Chris@559 517 error( RtError::NO_DEVICES_FOUND );
Chris@559 518 }
Chris@559 519
Chris@559 520 std::ostringstream ost;
Chris@559 521 if ( portNumber >= nDest ) {
Chris@559 522 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 523 errorString_ = ost.str();
Chris@559 524 error( RtError::INVALID_PARAMETER );
Chris@559 525 }
Chris@559 526
Chris@559 527 MIDIPortRef port;
Chris@559 528 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@565 529 OSStatus result = MIDIOutputPortCreate( data->client,
Chris@565 530 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
Chris@565 531 &port );
Chris@559 532 if ( result != noErr ) {
Chris@559 533 MIDIClientDispose( data->client );
Chris@559 534 errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port.";
Chris@559 535 error( RtError::DRIVER_ERROR );
Chris@559 536 }
Chris@559 537
Chris@559 538 // Get the desired output port identifier.
Chris@559 539 MIDIEndpointRef destination = MIDIGetDestination( portNumber );
Chris@559 540 if ( destination == NULL ) {
Chris@559 541 MIDIPortDispose( port );
Chris@559 542 MIDIClientDispose( data->client );
Chris@559 543 errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference.";
Chris@559 544 error( RtError::DRIVER_ERROR );
Chris@559 545 }
Chris@559 546
Chris@559 547 // Save our api-specific connection information.
Chris@559 548 data->port = port;
Chris@559 549 data->destinationId = destination;
Chris@559 550 connected_ = true;
Chris@559 551 }
Chris@559 552
Chris@559 553 void RtMidiOut :: closePort( void )
Chris@559 554 {
Chris@559 555 if ( connected_ ) {
Chris@559 556 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@559 557 MIDIPortDispose( data->port );
Chris@559 558 connected_ = false;
Chris@559 559 }
Chris@559 560 }
Chris@559 561
Chris@559 562 void RtMidiOut :: openVirtualPort( std::string portName )
Chris@559 563 {
Chris@559 564 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@559 565
Chris@559 566 if ( data->endpoint ) {
Chris@559 567 errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!";
Chris@559 568 error( RtError::WARNING );
Chris@559 569 return;
Chris@559 570 }
Chris@559 571
Chris@559 572 // Create a virtual MIDI output source.
Chris@559 573 MIDIEndpointRef endpoint;
Chris@559 574 OSStatus result = MIDISourceCreate( data->client,
Chris@559 575 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
Chris@559 576 &endpoint );
Chris@559 577 if ( result != noErr ) {
Chris@559 578 errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source.";
Chris@559 579 error( RtError::DRIVER_ERROR );
Chris@559 580 }
Chris@559 581
Chris@559 582 // Save our api-specific connection information.
Chris@559 583 data->endpoint = endpoint;
Chris@559 584 }
Chris@559 585
Chris@559 586 RtMidiOut :: ~RtMidiOut()
Chris@559 587 {
Chris@559 588 // Close a connection if it exists.
Chris@559 589 closePort();
Chris@559 590
Chris@559 591 // Cleanup.
Chris@559 592 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@559 593 MIDIClientDispose( data->client );
Chris@559 594 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
Chris@559 595 delete data;
Chris@559 596 }
Chris@559 597
Chris@559 598 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
Chris@559 599 {
Chris@565 600 // The CoreMidi documentation indicates a maximum PackList size of
Chris@565 601 // 64K, so we may need to break long sysex messages into pieces and
Chris@565 602 // send via separate lists.
Chris@559 603 unsigned int nBytes = message->size();
Chris@565 604 if ( nBytes == 0 ) {
Chris@565 605 errorString_ = "RtMidiOut::sendMessage: no data in message argument!";
Chris@565 606 error( RtError::WARNING );
Chris@565 607 return;
Chris@565 608 }
Chris@559 609
Chris@565 610 if ( nBytes > 3 && ( message->at(0) != 0xF0 ) ) {
Chris@565 611 errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?";
Chris@565 612 error( RtError::WARNING );
Chris@565 613 return;
Chris@565 614 }
Chris@565 615
Chris@565 616 unsigned int packetBytes, bytesLeft = nBytes;
Chris@565 617 unsigned int messageIndex = 0;
Chris@559 618 MIDITimeStamp timeStamp = 0;
Chris@559 619 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
Chris@559 620
Chris@565 621 while ( bytesLeft > 0 ) {
Chris@565 622
Chris@565 623 packetBytes = ( bytesLeft > 32736 ) ? 32736 : bytesLeft;
Chris@565 624 Byte buffer[packetBytes + 32]; // extra memory for other structure variables
Chris@565 625 MIDIPacketList *packetList = (MIDIPacketList *) buffer;
Chris@565 626 MIDIPacket *curPacket = MIDIPacketListInit( packetList );
Chris@565 627
Chris@565 628 curPacket = MIDIPacketListAdd( packetList, packetBytes+32, curPacket, timeStamp, packetBytes, (const Byte *) &message->at( messageIndex ) );
Chris@565 629 if ( !curPacket ) {
Chris@565 630 errorString_ = "RtMidiOut::sendMessage: could not allocate packet list";
Chris@565 631 error( RtError::DRIVER_ERROR );
Chris@559 632 }
Chris@565 633 messageIndex += packetBytes;
Chris@565 634 bytesLeft -= packetBytes;
Chris@559 635
Chris@565 636 // Send to any destinations that may have connected to us.
Chris@565 637 OSStatus result;
Chris@565 638 if ( data->endpoint ) {
Chris@565 639 result = MIDIReceived( data->endpoint, packetList );
Chris@565 640 if ( result != noErr ) {
Chris@565 641 errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
Chris@565 642 error( RtError::WARNING );
Chris@565 643 }
Chris@565 644 }
Chris@565 645
Chris@565 646 // And send to an explicit destination port if we're connected.
Chris@565 647 if ( connected_ ) {
Chris@565 648 result = MIDISend( data->port, data->destinationId, packetList );
Chris@565 649 if ( result != noErr ) {
Chris@565 650 errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
Chris@565 651 error( RtError::WARNING );
Chris@565 652 }
Chris@559 653 }
Chris@559 654 }
Chris@559 655 }
Chris@559 656
Chris@559 657 #endif // __MACOSX_CORE__
Chris@559 658
Chris@559 659
Chris@559 660 //*********************************************************************//
Chris@559 661 // API: LINUX ALSA SEQUENCER
Chris@559 662 //*********************************************************************//
Chris@559 663
Chris@559 664 // API information found at:
Chris@559 665 // - http://www.alsa-project.org/documentation.php#Library
Chris@559 666
Chris@559 667 #if defined(__LINUX_ALSASEQ__)
Chris@559 668
Chris@559 669 // The ALSA Sequencer API is based on the use of a callback function for
Chris@559 670 // MIDI input.
Chris@559 671 //
Chris@559 672 // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
Chris@559 673 // time stamps and other assorted fixes!!!
Chris@559 674
Chris@559 675 #include <pthread.h>
Chris@559 676 #include <sys/time.h>
Chris@559 677
Chris@559 678 // ALSA header file.
Chris@559 679 #include <alsa/asoundlib.h>
Chris@559 680
Chris@559 681 // A structure to hold variables related to the ALSA API
Chris@559 682 // implementation.
Chris@559 683 struct AlsaMidiData {
Chris@559 684 snd_seq_t *seq;
Chris@559 685 int vport;
Chris@559 686 snd_seq_port_subscribe_t *subscription;
Chris@559 687 snd_midi_event_t *coder;
Chris@559 688 unsigned int bufferSize;
Chris@559 689 unsigned char *buffer;
Chris@559 690 pthread_t thread;
Chris@559 691 unsigned long long lastTime;
Chris@559 692 int queue_id; // an input queue is needed to get timestamped events
Chris@559 693 };
Chris@559 694
Chris@559 695 #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
Chris@559 696
Chris@559 697 //*********************************************************************//
Chris@559 698 // API: LINUX ALSA
Chris@559 699 // Class Definitions: RtMidiIn
Chris@559 700 //*********************************************************************//
Chris@559 701
Chris@559 702 extern "C" void *alsaMidiHandler( void *ptr )
Chris@559 703 {
Chris@559 704 RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (ptr);
Chris@559 705 AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);
Chris@559 706
Chris@559 707 long nBytes;
Chris@559 708 unsigned long long time, lastTime;
Chris@559 709 bool continueSysex = false;
Chris@559 710 RtMidiIn::MidiMessage message;
Chris@559 711
Chris@559 712 snd_seq_event_t *ev;
Chris@559 713 int result;
Chris@559 714 apiData->bufferSize = 32;
Chris@559 715 result = snd_midi_event_new( 0, &apiData->coder );
Chris@559 716 if ( result < 0 ) {
Chris@559 717 data->doInput = false;
Chris@559 718 std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n";
Chris@559 719 return 0;
Chris@559 720 }
Chris@559 721 unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );
Chris@559 722 if ( buffer == NULL ) {
Chris@559 723 data->doInput = false;
Chris@559 724 std::cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n";
Chris@559 725 return 0;
Chris@559 726 }
Chris@559 727 snd_midi_event_init( apiData->coder );
Chris@559 728 snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages
Chris@559 729
Chris@559 730 while ( data->doInput ) {
Chris@559 731
Chris@559 732 if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {
Chris@559 733 // No data pending ... sleep a bit.
Chris@559 734 usleep( 1000 );
Chris@559 735 continue;
Chris@559 736 }
Chris@559 737
Chris@559 738 // If here, there should be data.
Chris@559 739 result = snd_seq_event_input( apiData->seq, &ev );
Chris@559 740 if ( result == -ENOSPC ) {
Chris@559 741 std::cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n";
Chris@559 742 continue;
Chris@559 743 }
Chris@559 744 else if ( result <= 0 ) {
Chris@559 745 std::cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n";
Chris@559 746 continue;
Chris@559 747 }
Chris@559 748
Chris@559 749 // This is a bit weird, but we now have to decode an ALSA MIDI
Chris@559 750 // event (back) into MIDI bytes. We'll ignore non-MIDI types.
Chris@565 751 if ( !continueSysex )
Chris@565 752 message.bytes.clear();
Chris@565 753
Chris@559 754 switch ( ev->type ) {
Chris@559 755
Chris@559 756 case SND_SEQ_EVENT_PORT_SUBSCRIBED:
Chris@559 757 #if defined(__RTMIDI_DEBUG__)
Chris@559 758 std::cout << "RtMidiIn::alsaMidiHandler: port connection made!\n";
Chris@559 759 #endif
Chris@559 760 break;
Chris@559 761
Chris@559 762 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
Chris@565 763 #if defined(__RTMIDI_DEBUG__)
Chris@690 764 SVDEBUG << "RtMidiIn::alsaMidiHandler: port connection has closed!\n";
Chris@565 765 // FIXME: this is called for all unsubscribe events, even ones
Chris@565 766 //not related to this particular connection. As it stands, I
Chris@565 767 //see no data provided in the "source" and "dest" fields so
Chris@565 768 //there is nothing we can do about this at this time.
Chris@687 769 // std::cout << "sender = " << ev->source.client << ", dest = " << ev->dest.port << endl;
Chris@565 770 #endif
Chris@565 771 //data->doInput = false;
Chris@559 772 break;
Chris@559 773
Chris@559 774 case SND_SEQ_EVENT_QFRAME: // MIDI time code
Chris@559 775 if ( data->ignoreFlags & 0x02 ) break;
Chris@559 776
Chris@559 777 case SND_SEQ_EVENT_TICK: // MIDI timing tick
Chris@559 778 if ( data->ignoreFlags & 0x02 ) break;
Chris@559 779
Chris@559 780 case SND_SEQ_EVENT_SENSING: // Active sensing
Chris@559 781 if ( data->ignoreFlags & 0x04 ) break;
Chris@559 782
Chris@559 783 case SND_SEQ_EVENT_SYSEX:
Chris@559 784 if ( (data->ignoreFlags & 0x01) ) break;
Chris@559 785 if ( ev->data.ext.len > apiData->bufferSize ) {
Chris@559 786 apiData->bufferSize = ev->data.ext.len;
Chris@559 787 free( buffer );
Chris@559 788 buffer = (unsigned char *) malloc( apiData->bufferSize );
Chris@559 789 if ( buffer == NULL ) {
Chris@559 790 data->doInput = false;
Chris@559 791 std::cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n";
Chris@559 792 break;
Chris@559 793 }
Chris@559 794 }
Chris@559 795
Chris@559 796 default:
Chris@559 797 nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
Chris@559 798 if ( nBytes <= 0 ) {
Chris@559 799 #if defined(__RTMIDI_DEBUG__)
Chris@559 800 std::cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
Chris@559 801 #endif
Chris@559 802 break;
Chris@559 803 }
Chris@559 804
Chris@559 805 // The ALSA sequencer has a maximum buffer size for MIDI sysex
Chris@559 806 // events of 256 bytes. If a device sends sysex messages larger
Chris@559 807 // than this, they are segmented into 256 byte chunks. So,
Chris@559 808 // we'll watch for this and concatenate sysex chunks into a
Chris@559 809 // single sysex message if necessary.
Chris@559 810 if ( !continueSysex )
Chris@559 811 message.bytes.assign( buffer, &buffer[nBytes] );
Chris@559 812 else
Chris@559 813 message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
Chris@559 814
Chris@565 815 continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
Chris@559 816 if ( continueSysex )
Chris@559 817 break;
Chris@559 818
Chris@559 819 // Calculate the time stamp:
Chris@559 820 message.timeStamp = 0.0;
Chris@559 821
Chris@559 822 // Method 1: Use the system time.
Chris@559 823 //(void)gettimeofday(&tv, (struct timezone *)NULL);
Chris@559 824 //time = (tv.tv_sec * 1000000) + tv.tv_usec;
Chris@559 825
Chris@559 826 // Method 2: Use the ALSA sequencer event time data.
Chris@559 827 // (thanks to Pedro Lopez-Cabanillas!).
Chris@559 828 time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
Chris@559 829 lastTime = time;
Chris@559 830 time -= apiData->lastTime;
Chris@559 831 apiData->lastTime = lastTime;
Chris@559 832 if ( data->firstMessage == true )
Chris@559 833 data->firstMessage = false;
Chris@559 834 else
Chris@559 835 message.timeStamp = time * 0.000001;
Chris@559 836 }
Chris@559 837
Chris@559 838 snd_seq_free_event(ev);
Chris@559 839 if ( message.bytes.size() == 0 ) continue;
Chris@559 840
Chris@565 841 if ( data->usingCallback && !continueSysex ) {
Chris@559 842 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 843 callback( message.timeStamp, &message.bytes, data->userData );
Chris@559 844 }
Chris@559 845 else {
Chris@559 846 // As long as we haven't reached our queue size limit, push the message.
Chris@559 847 if ( data->queueLimit > data->queue.size() )
Chris@559 848 data->queue.push( message );
Chris@559 849 else
Chris@559 850 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
Chris@559 851 }
Chris@559 852 }
Chris@559 853
Chris@559 854 if ( buffer ) free( buffer );
Chris@559 855 snd_midi_event_free( apiData->coder );
Chris@559 856 apiData->coder = 0;
Chris@559 857 return 0;
Chris@559 858 }
Chris@559 859
Chris@565 860 void RtMidiIn :: initialize( const std::string& clientName )
Chris@559 861 {
Chris@559 862 // Set up the ALSA sequencer client.
Chris@565 863 snd_seq_t *seq;
Chris@559 864 int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
Chris@559 865 if ( result < 0 ) {
Chris@559 866 errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object.";
Chris@559 867 error( RtError::DRIVER_ERROR );
Chris@559 868 }
Chris@559 869
Chris@559 870 // Set client name.
Chris@565 871 snd_seq_set_client_name( seq, clientName.c_str() );
Chris@559 872
Chris@559 873 // Save our api-specific connection information.
Chris@559 874 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
Chris@559 875 data->seq = seq;
Chris@559 876 data->vport = -1;
Chris@559 877 apiData_ = (void *) data;
Chris@559 878 inputData_.apiData = (void *) data;
Chris@559 879
Chris@559 880 // Create the input queue
Chris@559 881 data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue");
Chris@559 882 // Set arbitrary tempo (mm=100) and resolution (240)
Chris@559 883 snd_seq_queue_tempo_t *qtempo;
Chris@559 884 snd_seq_queue_tempo_alloca(&qtempo);
Chris@559 885 snd_seq_queue_tempo_set_tempo(qtempo, 600000);
Chris@559 886 snd_seq_queue_tempo_set_ppq(qtempo, 240);
Chris@559 887 snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo);
Chris@559 888 snd_seq_drain_output(data->seq);
Chris@559 889 }
Chris@559 890
Chris@559 891 // This function is used to count or get the pinfo structure for a given port number.
Chris@559 892 unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )
Chris@559 893 {
Chris@559 894 snd_seq_client_info_t *cinfo;
Chris@559 895 int client;
Chris@559 896 int count = 0;
Chris@559 897 snd_seq_client_info_alloca( &cinfo );
Chris@559 898
Chris@559 899 snd_seq_client_info_set_client( cinfo, -1 );
Chris@559 900 while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {
Chris@559 901 client = snd_seq_client_info_get_client( cinfo );
Chris@559 902 if ( client == 0 ) continue;
Chris@559 903 // Reset query info
Chris@559 904 snd_seq_port_info_set_client( pinfo, client );
Chris@559 905 snd_seq_port_info_set_port( pinfo, -1 );
Chris@559 906 while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
Chris@565 907 unsigned int atyp = snd_seq_port_info_get_type( pinfo );
Chris@565 908 if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue;
Chris@565 909 unsigned int caps = snd_seq_port_info_get_capability( pinfo );
Chris@565 910 if ( ( caps & type ) != type ) continue;
Chris@559 911 if ( count == portNumber ) return 1;
Chris@559 912 count++;
Chris@559 913 }
Chris@559 914 }
Chris@559 915
Chris@559 916 // If a negative portNumber was used, return the port count.
Chris@559 917 if ( portNumber < 0 ) return count;
Chris@559 918 return 0;
Chris@559 919 }
Chris@559 920
Chris@565 921 void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName )
Chris@559 922 {
Chris@559 923 if ( connected_ ) {
Chris@559 924 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
Chris@559 925 error( RtError::WARNING );
Chris@559 926 return;
Chris@559 927 }
Chris@559 928
Chris@559 929 unsigned int nSrc = this->getPortCount();
Chris@559 930 if (nSrc < 1) {
Chris@559 931 errorString_ = "RtMidiIn::openPort: no MIDI input sources found!";
Chris@559 932 error( RtError::NO_DEVICES_FOUND );
Chris@559 933 }
Chris@559 934
Chris@565 935 snd_seq_port_info_t *pinfo;
Chris@565 936 snd_seq_port_info_alloca( &pinfo );
Chris@559 937 std::ostringstream ost;
Chris@559 938 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 939 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {
Chris@559 940 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 941 errorString_ = ost.str();
Chris@559 942 error( RtError::INVALID_PARAMETER );
Chris@559 943 }
Chris@559 944
Chris@559 945
Chris@559 946 snd_seq_addr_t sender, receiver;
Chris@559 947 sender.client = snd_seq_port_info_get_client( pinfo );
Chris@559 948 sender.port = snd_seq_port_info_get_port( pinfo );
Chris@559 949 receiver.client = snd_seq_client_id( data->seq );
Chris@559 950 if ( data->vport < 0 ) {
Chris@559 951 snd_seq_port_info_set_client( pinfo, 0 );
Chris@559 952 snd_seq_port_info_set_port( pinfo, 0 );
Chris@559 953 snd_seq_port_info_set_capability( pinfo,
Chris@559 954 SND_SEQ_PORT_CAP_WRITE |
Chris@559 955 SND_SEQ_PORT_CAP_SUBS_WRITE );
Chris@559 956 snd_seq_port_info_set_type( pinfo,
Chris@559 957 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
Chris@559 958 SND_SEQ_PORT_TYPE_APPLICATION );
Chris@559 959 snd_seq_port_info_set_midi_channels(pinfo, 16);
Chris@559 960 snd_seq_port_info_set_timestamping(pinfo, 1);
Chris@559 961 snd_seq_port_info_set_timestamp_real(pinfo, 1);
Chris@559 962 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
Chris@565 963 snd_seq_port_info_set_name(pinfo, portName.c_str() );
Chris@559 964 data->vport = snd_seq_create_port(data->seq, pinfo);
Chris@559 965
Chris@559 966 if ( data->vport < 0 ) {
Chris@559 967 errorString_ = "RtMidiIn::openPort: ALSA error creating input port.";
Chris@559 968 error( RtError::DRIVER_ERROR );
Chris@559 969 }
Chris@559 970 }
Chris@559 971
Chris@559 972 receiver.port = data->vport;
Chris@559 973
Chris@559 974 // Make subscription
Chris@559 975 snd_seq_port_subscribe_malloc( &data->subscription );
Chris@559 976 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
Chris@559 977 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
Chris@559 978 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
Chris@559 979 errorString_ = "RtMidiIn::openPort: ALSA error making port connection.";
Chris@559 980 error( RtError::DRIVER_ERROR );
Chris@559 981 }
Chris@559 982
Chris@559 983 if ( inputData_.doInput == false ) {
Chris@559 984 // Start the input queue
Chris@559 985 snd_seq_start_queue( data->seq, data->queue_id, NULL );
Chris@559 986 snd_seq_drain_output( data->seq );
Chris@559 987 // Start our MIDI input thread.
Chris@559 988 pthread_attr_t attr;
Chris@559 989 pthread_attr_init(&attr);
Chris@559 990 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
Chris@559 991 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
Chris@559 992
Chris@559 993 inputData_.doInput = true;
Chris@559 994 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
Chris@559 995 pthread_attr_destroy(&attr);
Chris@559 996 if (err) {
Chris@559 997 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@559 998 snd_seq_port_subscribe_free( data->subscription );
Chris@559 999 inputData_.doInput = false;
Chris@559 1000 errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!";
Chris@559 1001 error( RtError::THREAD_ERROR );
Chris@559 1002 }
Chris@559 1003 }
Chris@559 1004
Chris@559 1005 connected_ = true;
Chris@559 1006 }
Chris@559 1007
Chris@559 1008 void RtMidiIn :: openVirtualPort( std::string portName )
Chris@559 1009 {
Chris@559 1010 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1011 if ( data->vport < 0 ) {
Chris@559 1012 snd_seq_port_info_t *pinfo;
Chris@559 1013 snd_seq_port_info_alloca( &pinfo );
Chris@559 1014 snd_seq_port_info_set_capability( pinfo,
Chris@559 1015 SND_SEQ_PORT_CAP_WRITE |
Chris@559 1016 SND_SEQ_PORT_CAP_SUBS_WRITE );
Chris@559 1017 snd_seq_port_info_set_type( pinfo,
Chris@559 1018 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
Chris@559 1019 SND_SEQ_PORT_TYPE_APPLICATION );
Chris@559 1020 snd_seq_port_info_set_midi_channels(pinfo, 16);
Chris@559 1021 snd_seq_port_info_set_timestamping(pinfo, 1);
Chris@559 1022 snd_seq_port_info_set_timestamp_real(pinfo, 1);
Chris@559 1023 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
Chris@559 1024 snd_seq_port_info_set_name(pinfo, portName.c_str());
Chris@559 1025 data->vport = snd_seq_create_port(data->seq, pinfo);
Chris@559 1026
Chris@559 1027 if ( data->vport < 0 ) {
Chris@559 1028 errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port.";
Chris@559 1029 error( RtError::DRIVER_ERROR );
Chris@559 1030 }
Chris@559 1031 }
Chris@559 1032
Chris@559 1033 if ( inputData_.doInput == false ) {
Chris@559 1034 // Start the input queue
Chris@559 1035 snd_seq_start_queue( data->seq, data->queue_id, NULL );
Chris@559 1036 snd_seq_drain_output( data->seq );
Chris@559 1037 // Start our MIDI input thread.
Chris@559 1038 pthread_attr_t attr;
Chris@559 1039 pthread_attr_init(&attr);
Chris@559 1040 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
Chris@559 1041 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
Chris@559 1042
Chris@559 1043 inputData_.doInput = true;
Chris@559 1044 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
Chris@559 1045 pthread_attr_destroy(&attr);
Chris@559 1046 if (err) {
Chris@559 1047 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@559 1048 snd_seq_port_subscribe_free( data->subscription );
Chris@559 1049 inputData_.doInput = false;
Chris@559 1050 errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!";
Chris@559 1051 error( RtError::THREAD_ERROR );
Chris@559 1052 }
Chris@559 1053 }
Chris@559 1054 }
Chris@559 1055
Chris@559 1056 void RtMidiIn :: closePort( void )
Chris@559 1057 {
Chris@559 1058 if ( connected_ ) {
Chris@559 1059 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1060 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@559 1061 snd_seq_port_subscribe_free( data->subscription );
Chris@559 1062 // Stop the input queue
Chris@559 1063 snd_seq_stop_queue( data->seq, data->queue_id, NULL );
Chris@559 1064 snd_seq_drain_output( data->seq );
Chris@559 1065 connected_ = false;
Chris@559 1066 }
Chris@559 1067 }
Chris@559 1068
Chris@559 1069 RtMidiIn :: ~RtMidiIn()
Chris@559 1070 {
Chris@559 1071 // Close a connection if it exists.
Chris@559 1072 closePort();
Chris@559 1073
Chris@559 1074 // Shutdown the input thread.
Chris@559 1075 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1076 if ( inputData_.doInput ) {
Chris@559 1077 inputData_.doInput = false;
Chris@559 1078 pthread_join( data->thread, NULL );
Chris@559 1079 }
Chris@559 1080
Chris@559 1081 // Cleanup.
Chris@559 1082 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
Chris@559 1083 snd_seq_free_queue( data->seq, data->queue_id );
Chris@559 1084 snd_seq_close( data->seq );
Chris@559 1085 delete data;
Chris@559 1086 }
Chris@559 1087
Chris@559 1088 unsigned int RtMidiIn :: getPortCount()
Chris@559 1089 {
Chris@559 1090 snd_seq_port_info_t *pinfo;
Chris@559 1091 snd_seq_port_info_alloca( &pinfo );
Chris@559 1092
Chris@559 1093 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1094 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );
Chris@559 1095 }
Chris@559 1096
Chris@559 1097 std::string RtMidiIn :: getPortName( unsigned int portNumber )
Chris@559 1098 {
Chris@565 1099 snd_seq_client_info_t *cinfo;
Chris@565 1100 snd_seq_port_info_t *pinfo;
Chris@565 1101 snd_seq_client_info_alloca( &cinfo );
Chris@565 1102 snd_seq_port_info_alloca( &pinfo );
Chris@559 1103
Chris@559 1104 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1105 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
Chris@565 1106 int cnum = snd_seq_port_info_get_client( pinfo );
Chris@565 1107 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
Chris@565 1108 std::ostringstream os;
Chris@565 1109 os << snd_seq_client_info_get_name( cinfo );
Chris@565 1110 os << ":";
Chris@565 1111 os << snd_seq_port_info_get_port( pinfo );
Chris@565 1112 std::string stringName = os.str();
Chris@559 1113 return stringName;
Chris@559 1114 }
Chris@559 1115
Chris@559 1116 // If we get here, we didn't find a match.
Chris@559 1117 errorString_ = "RtMidiIn::getPortName: error looking for port name!";
Chris@559 1118 error( RtError::INVALID_PARAMETER );
Chris@559 1119 return 0;
Chris@559 1120 }
Chris@559 1121
Chris@559 1122 //*********************************************************************//
Chris@559 1123 // API: LINUX ALSA
Chris@559 1124 // Class Definitions: RtMidiOut
Chris@559 1125 //*********************************************************************//
Chris@559 1126
Chris@559 1127 unsigned int RtMidiOut :: getPortCount()
Chris@559 1128 {
Chris@559 1129 snd_seq_port_info_t *pinfo;
Chris@559 1130 snd_seq_port_info_alloca( &pinfo );
Chris@559 1131
Chris@559 1132 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1133 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );
Chris@559 1134 }
Chris@559 1135
Chris@559 1136 std::string RtMidiOut :: getPortName( unsigned int portNumber )
Chris@559 1137 {
Chris@565 1138 snd_seq_client_info_t *cinfo;
Chris@565 1139 snd_seq_port_info_t *pinfo;
Chris@565 1140 snd_seq_client_info_alloca( &cinfo );
Chris@565 1141 snd_seq_port_info_alloca( &pinfo );
Chris@559 1142
Chris@559 1143 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1144 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
Chris@565 1145 int cnum = snd_seq_port_info_get_client(pinfo);
Chris@565 1146 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
Chris@565 1147 std::ostringstream os;
Chris@565 1148 os << snd_seq_client_info_get_name(cinfo);
Chris@565 1149 os << ":";
Chris@565 1150 os << snd_seq_port_info_get_port(pinfo);
Chris@565 1151 std::string stringName = os.str();
Chris@559 1152 return stringName;
Chris@559 1153 }
Chris@559 1154
Chris@559 1155 // If we get here, we didn't find a match.
Chris@559 1156 errorString_ = "RtMidiOut::getPortName: error looking for port name!";
Chris@559 1157 error( RtError::INVALID_PARAMETER );
Chris@559 1158 return 0;
Chris@559 1159 }
Chris@559 1160
Chris@565 1161 void RtMidiOut :: initialize( const std::string& clientName )
Chris@559 1162 {
Chris@559 1163 // Set up the ALSA sequencer client.
Chris@565 1164 snd_seq_t *seq;
Chris@565 1165 int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
Chris@559 1166 if ( result < 0 ) {
Chris@559 1167 errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object.";
Chris@559 1168 error( RtError::DRIVER_ERROR );
Chris@559 1169 }
Chris@559 1170
Chris@559 1171 // Set client name.
Chris@565 1172 snd_seq_set_client_name( seq, clientName.c_str() );
Chris@559 1173
Chris@559 1174 // Save our api-specific connection information.
Chris@559 1175 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
Chris@559 1176 data->seq = seq;
Chris@559 1177 data->vport = -1;
Chris@559 1178 data->bufferSize = 32;
Chris@559 1179 data->coder = 0;
Chris@559 1180 data->buffer = 0;
Chris@559 1181 result = snd_midi_event_new( data->bufferSize, &data->coder );
Chris@559 1182 if ( result < 0 ) {
Chris@559 1183 delete data;
Chris@559 1184 errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n";
Chris@559 1185 error( RtError::DRIVER_ERROR );
Chris@559 1186 }
Chris@559 1187 data->buffer = (unsigned char *) malloc( data->bufferSize );
Chris@559 1188 if ( data->buffer == NULL ) {
Chris@559 1189 delete data;
Chris@559 1190 errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n";
Chris@559 1191 error( RtError::MEMORY_ERROR );
Chris@559 1192 }
Chris@559 1193 snd_midi_event_init( data->coder );
Chris@559 1194 apiData_ = (void *) data;
Chris@559 1195 }
Chris@559 1196
Chris@565 1197 void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName )
Chris@559 1198 {
Chris@559 1199 if ( connected_ ) {
Chris@559 1200 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
Chris@559 1201 error( RtError::WARNING );
Chris@559 1202 return;
Chris@559 1203 }
Chris@559 1204
Chris@559 1205 unsigned int nSrc = this->getPortCount();
Chris@559 1206 if (nSrc < 1) {
Chris@559 1207 errorString_ = "RtMidiOut::openPort: no MIDI output sources found!";
Chris@559 1208 error( RtError::NO_DEVICES_FOUND );
Chris@559 1209 }
Chris@559 1210
Chris@565 1211 snd_seq_port_info_t *pinfo;
Chris@565 1212 snd_seq_port_info_alloca( &pinfo );
Chris@559 1213 std::ostringstream ost;
Chris@559 1214 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1215 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {
Chris@559 1216 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1217 errorString_ = ost.str();
Chris@559 1218 error( RtError::INVALID_PARAMETER );
Chris@559 1219 }
Chris@559 1220
Chris@559 1221 snd_seq_addr_t sender, receiver;
Chris@559 1222 receiver.client = snd_seq_port_info_get_client( pinfo );
Chris@559 1223 receiver.port = snd_seq_port_info_get_port( pinfo );
Chris@559 1224 sender.client = snd_seq_client_id( data->seq );
Chris@559 1225
Chris@559 1226 if ( data->vport < 0 ) {
Chris@565 1227 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
Chris@559 1228 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
Chris@559 1229 SND_SEQ_PORT_TYPE_MIDI_GENERIC );
Chris@559 1230 if ( data->vport < 0 ) {
Chris@559 1231 errorString_ = "RtMidiOut::openPort: ALSA error creating output port.";
Chris@559 1232 error( RtError::DRIVER_ERROR );
Chris@559 1233 }
Chris@559 1234 }
Chris@559 1235
Chris@559 1236 sender.port = data->vport;
Chris@559 1237
Chris@559 1238 // Make subscription
Chris@559 1239 snd_seq_port_subscribe_malloc( &data->subscription );
Chris@559 1240 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
Chris@559 1241 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
Chris@559 1242 snd_seq_port_subscribe_set_time_update(data->subscription, 1);
Chris@559 1243 snd_seq_port_subscribe_set_time_real(data->subscription, 1);
Chris@559 1244 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
Chris@559 1245 errorString_ = "RtMidiOut::openPort: ALSA error making port connection.";
Chris@559 1246 error( RtError::DRIVER_ERROR );
Chris@559 1247 }
Chris@559 1248
Chris@559 1249 connected_ = true;
Chris@559 1250 }
Chris@559 1251
Chris@559 1252 void RtMidiOut :: closePort( void )
Chris@559 1253 {
Chris@559 1254 if ( connected_ ) {
Chris@559 1255 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1256 snd_seq_unsubscribe_port( data->seq, data->subscription );
Chris@559 1257 snd_seq_port_subscribe_free( data->subscription );
Chris@559 1258 connected_ = false;
Chris@559 1259 }
Chris@559 1260 }
Chris@559 1261
Chris@559 1262 void RtMidiOut :: openVirtualPort( std::string portName )
Chris@559 1263 {
Chris@559 1264 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1265 if ( data->vport < 0 ) {
Chris@559 1266 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
Chris@559 1267 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
Chris@559 1268 SND_SEQ_PORT_TYPE_MIDI_GENERIC );
Chris@559 1269
Chris@559 1270 if ( data->vport < 0 ) {
Chris@559 1271 errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port.";
Chris@559 1272 error( RtError::DRIVER_ERROR );
Chris@559 1273 }
Chris@559 1274 }
Chris@559 1275 }
Chris@559 1276
Chris@559 1277 RtMidiOut :: ~RtMidiOut()
Chris@559 1278 {
Chris@559 1279 // Close a connection if it exists.
Chris@559 1280 closePort();
Chris@559 1281
Chris@559 1282 // Cleanup.
Chris@559 1283 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1284 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
Chris@559 1285 if ( data->coder ) snd_midi_event_free( data->coder );
Chris@559 1286 if ( data->buffer ) free( data->buffer );
Chris@559 1287 snd_seq_close( data->seq );
Chris@559 1288 delete data;
Chris@559 1289 }
Chris@559 1290
Chris@559 1291 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
Chris@559 1292 {
Chris@559 1293 int result;
Chris@559 1294 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
Chris@559 1295 unsigned int nBytes = message->size();
Chris@559 1296 if ( nBytes > data->bufferSize ) {
Chris@559 1297 data->bufferSize = nBytes;
Chris@559 1298 result = snd_midi_event_resize_buffer ( data->coder, nBytes);
Chris@559 1299 if ( result != 0 ) {
Chris@559 1300 errorString_ = "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer.";
Chris@559 1301 error( RtError::DRIVER_ERROR );
Chris@559 1302 }
Chris@559 1303 free (data->buffer);
Chris@559 1304 data->buffer = (unsigned char *) malloc( data->bufferSize );
Chris@559 1305 if ( data->buffer == NULL ) {
Chris@559 1306 errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n";
Chris@559 1307 error( RtError::MEMORY_ERROR );
Chris@559 1308 }
Chris@559 1309 }
Chris@559 1310
Chris@559 1311 snd_seq_event_t ev;
Chris@559 1312 snd_seq_ev_clear(&ev);
Chris@559 1313 snd_seq_ev_set_source(&ev, data->vport);
Chris@559 1314 snd_seq_ev_set_subs(&ev);
Chris@559 1315 snd_seq_ev_set_direct(&ev);
Chris@559 1316 for ( unsigned int i=0; i<nBytes; i++ ) data->buffer[i] = message->at(i);
Chris@559 1317 result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
Chris@559 1318 if ( result < (int)nBytes ) {
Chris@559 1319 errorString_ = "RtMidiOut::sendMessage: event parsing error!";
Chris@559 1320 error( RtError::WARNING );
Chris@559 1321 return;
Chris@559 1322 }
Chris@559 1323
Chris@559 1324 // Send the event.
Chris@559 1325 result = snd_seq_event_output(data->seq, &ev);
Chris@559 1326 if ( result < 0 ) {
Chris@559 1327 errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port.";
Chris@559 1328 error( RtError::WARNING );
Chris@559 1329 }
Chris@559 1330 snd_seq_drain_output(data->seq);
Chris@559 1331 }
Chris@559 1332
Chris@559 1333 #endif // __LINUX_ALSA__
Chris@559 1334
Chris@559 1335
Chris@559 1336 //*********************************************************************//
Chris@559 1337 // API: IRIX MD
Chris@559 1338 //*********************************************************************//
Chris@559 1339
Chris@559 1340 // API information gleamed from:
Chris@559 1341 // http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro
Chris@559 1342
Chris@559 1343 // If the Makefile doesn't work, try the following:
Chris@559 1344 // CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd
Chris@559 1345 // CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd
Chris@559 1346 // CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd
Chris@559 1347 // CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd
Chris@559 1348
Chris@559 1349 #if defined(__IRIX_MD__)
Chris@559 1350
Chris@559 1351 #include <pthread.h>
Chris@559 1352 #include <sys/time.h>
Chris@559 1353 #include <unistd.h>
Chris@559 1354
Chris@559 1355 // Irix MIDI header file.
Chris@559 1356 #include <dmedia/midi.h>
Chris@559 1357
Chris@559 1358 // A structure to hold variables related to the IRIX API
Chris@559 1359 // implementation.
Chris@559 1360 struct IrixMidiData {
Chris@559 1361 MDport port;
Chris@559 1362 pthread_t thread;
Chris@559 1363 };
Chris@559 1364
Chris@559 1365 //*********************************************************************//
Chris@559 1366 // API: IRIX
Chris@559 1367 // Class Definitions: RtMidiIn
Chris@559 1368 //*********************************************************************//
Chris@559 1369
Chris@559 1370 extern "C" void *irixMidiHandler( void *ptr )
Chris@559 1371 {
Chris@559 1372 RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (ptr);
Chris@559 1373 IrixMidiData *apiData = static_cast<IrixMidiData *> (data->apiData);
Chris@559 1374
Chris@559 1375 bool continueSysex = false;
Chris@559 1376 unsigned char status;
Chris@559 1377 unsigned short size;
Chris@559 1378 MDevent event;
Chris@559 1379 int fd = mdGetFd( apiData->port );
Chris@559 1380 if ( fd < 0 ) {
Chris@559 1381 data->doInput = false;
Chris@559 1382 std::cerr << "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n";
Chris@559 1383 return 0;
Chris@559 1384 }
Chris@559 1385
Chris@559 1386 fd_set mask, rmask;
Chris@559 1387 FD_ZERO( &mask );
Chris@559 1388 FD_SET( fd, &mask );
Chris@559 1389 struct timeval timeout = {0, 0};
Chris@559 1390 RtMidiIn::MidiMessage message;
Chris@559 1391 int result;
Chris@559 1392
Chris@559 1393 while ( data->doInput ) {
Chris@559 1394
Chris@559 1395 rmask = mask;
Chris@559 1396 timeout.tv_sec = 0;
Chris@559 1397 timeout.tv_usec = 0;
Chris@559 1398 if ( select( fd+1, &rmask, NULL, NULL, &timeout ) <= 0 ) {
Chris@559 1399 // No data pending ... sleep a bit.
Chris@559 1400 usleep( 1000 );
Chris@559 1401 continue;
Chris@559 1402 }
Chris@559 1403
Chris@559 1404 // If here, there should be data.
Chris@559 1405 result = mdReceive( apiData->port, &event, 1);
Chris@559 1406 if ( result <= 0 ) {
Chris@559 1407 std::cerr << "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n";
Chris@559 1408 continue;
Chris@559 1409 }
Chris@559 1410
Chris@559 1411 message.timeStamp = event.stamp * 0.000000001;
Chris@559 1412
Chris@559 1413 size = 0;
Chris@559 1414 status = event.msg[0];
Chris@559 1415 if ( !(status & 0x80) ) continue;
Chris@559 1416 if ( status == 0xF0 ) {
Chris@559 1417 // Sysex message ... can be segmented across multiple messages.
Chris@559 1418 if ( !(data->ignoreFlags & 0x01) ) {
Chris@559 1419 if ( continueSysex ) {
Chris@559 1420 // We have a continuing, segmented sysex message. Append
Chris@559 1421 // the new bytes to our existing message.
Chris@559 1422 for ( int i=0; i<event.msglen; i++ )
Chris@559 1423 message.bytes.push_back( event.sysexmsg[i] );
Chris@559 1424 if ( event.sysexmsg[event.msglen-1] == 0xF7 ) continueSysex = false;
Chris@559 1425 if ( !continueSysex ) {
Chris@559 1426 // If not a continuing sysex message, invoke the user callback function or queue the message.
Chris@559 1427 if ( data->usingCallback && message.bytes.size() > 0 ) {
Chris@559 1428 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 1429 callback( message.timeStamp, &message.bytes, data->userData );
Chris@559 1430 }
Chris@559 1431 else {
Chris@559 1432 // As long as we haven't reached our queue size limit, push the message.
Chris@559 1433 if ( data->queueLimit > data->queue.size() )
Chris@559 1434 data->queue.push( message );
Chris@559 1435 else
Chris@559 1436 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
Chris@559 1437 }
Chris@559 1438 message.bytes.clear();
Chris@559 1439 }
Chris@559 1440 }
Chris@559 1441 }
Chris@559 1442 mdFree( NULL );
Chris@559 1443 continue;
Chris@559 1444 }
Chris@559 1445 else if ( status < 0xC0 ) size = 3;
Chris@559 1446 else if ( status < 0xE0 ) size = 2;
Chris@559 1447 else if ( status < 0xF0 ) size = 3;
Chris@559 1448 else if ( status < 0xF3 ) {
Chris@559 1449 if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) {
Chris@559 1450 // A MIDI time code message and we're not ignoring it.
Chris@559 1451 size = 3;
Chris@559 1452 }
Chris@559 1453 }
Chris@559 1454 else if ( status == 0xF3 ) size = 2;
Chris@559 1455 else if ( status == 0xF8 ) {
Chris@559 1456 if ( !(data->ignoreFlags & 0x02) ) {
Chris@559 1457 // A MIDI timing tick message and we're not ignoring it.
Chris@559 1458 size = 1;
Chris@559 1459 }
Chris@559 1460 }
Chris@559 1461 else if ( status == 0xFE ) { // MIDI active sensing
Chris@559 1462 if ( !(data->ignoreFlags & 0x04) )
Chris@559 1463 size = 1;
Chris@559 1464 }
Chris@559 1465 else size = 1;
Chris@559 1466
Chris@559 1467 // Copy the MIDI data to our vector.
Chris@559 1468 if ( size ) {
Chris@559 1469 message.bytes.assign( &event.msg[0], &event.msg[size] );
Chris@559 1470 // Invoke the user callback function or queue the message.
Chris@559 1471 if ( data->usingCallback ) {
Chris@559 1472 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 1473 callback( message.timeStamp, &message.bytes, data->userData );
Chris@559 1474 }
Chris@559 1475 else {
Chris@559 1476 // As long as we haven't reached our queue size limit, push the message.
Chris@559 1477 if ( data->queueLimit > data->queue.size() )
Chris@559 1478 data->queue.push( message );
Chris@559 1479 else
Chris@559 1480 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
Chris@559 1481 }
Chris@559 1482 message.bytes.clear();
Chris@559 1483 }
Chris@559 1484 }
Chris@559 1485
Chris@559 1486 return 0;
Chris@559 1487 }
Chris@559 1488
Chris@565 1489 void RtMidiIn :: initialize( const std::string& /*clientName*/ )
Chris@559 1490 {
Chris@559 1491 // Initialize the Irix MIDI system. At the moment, we will not
Chris@559 1492 // worry about a return value of zero (ports) because there is a
Chris@559 1493 // chance the user could plug something in after instantiation.
Chris@559 1494 int nPorts = mdInit();
Chris@559 1495
Chris@559 1496 // Create our api-specific connection information.
Chris@559 1497 IrixMidiData *data = (IrixMidiData *) new IrixMidiData;
Chris@559 1498 apiData_ = (void *) data;
Chris@559 1499 inputData_.apiData = (void *) data;
Chris@559 1500 }
Chris@559 1501
Chris@565 1502 void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@559 1503 {
Chris@559 1504 if ( connected_ ) {
Chris@559 1505 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
Chris@559 1506 error( RtError::WARNING );
Chris@559 1507 return;
Chris@559 1508 }
Chris@559 1509
Chris@559 1510 int nPorts = mdInit();
Chris@559 1511 if (nPorts < 1) {
Chris@559 1512 errorString_ = "RtMidiIn::openPort: no Irix MIDI input sources found!";
Chris@559 1513 error( RtError::NO_DEVICES_FOUND );
Chris@559 1514 }
Chris@559 1515
Chris@559 1516 std::ostringstream ost;
Chris@559 1517 if ( portNumber >= nPorts ) {
Chris@559 1518 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1519 errorString_ = ost.str();
Chris@559 1520 error( RtError::INVALID_PARAMETER );
Chris@559 1521 }
Chris@559 1522
Chris@559 1523 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
Chris@559 1524 data->port = mdOpenInPort( mdGetName(portNumber) );
Chris@559 1525 if ( data->port == NULL ) {
Chris@559 1526 ost << "RtMidiIn::openPort: Irix error opening the port (" << portNumber << ").";
Chris@559 1527 errorString_ = ost.str();
Chris@559 1528 error( RtError::DRIVER_ERROR );
Chris@559 1529 }
Chris@559 1530 mdSetStampMode(data->port, MD_DELTASTAMP);
Chris@559 1531
Chris@559 1532 // Start our MIDI input thread.
Chris@559 1533 pthread_attr_t attr;
Chris@559 1534 pthread_attr_init(&attr);
Chris@559 1535 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
Chris@559 1536 pthread_attr_setschedpolicy(&attr, SCHED_RR);
Chris@559 1537
Chris@559 1538 inputData_.doInput = true;
Chris@559 1539 int err = pthread_create(&data->thread, &attr, irixMidiHandler, &inputData_);
Chris@559 1540 pthread_attr_destroy(&attr);
Chris@559 1541 if (err) {
Chris@559 1542 mdClosePort( data->port );
Chris@559 1543 inputData_.doInput = false;
Chris@559 1544 errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!";
Chris@559 1545 error( RtError::THREAD_ERROR );
Chris@559 1546 }
Chris@559 1547
Chris@559 1548 connected_ = true;
Chris@559 1549 }
Chris@559 1550
Chris@559 1551 void RtMidiIn :: openVirtualPort( std::string portName )
Chris@559 1552 {
Chris@559 1553 // This function cannot be implemented for the Irix MIDI API.
Chris@559 1554 errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!";
Chris@559 1555 error( RtError::WARNING );
Chris@559 1556 }
Chris@559 1557
Chris@559 1558 void RtMidiIn :: closePort( void )
Chris@559 1559 {
Chris@559 1560 if ( connected_ ) {
Chris@559 1561 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
Chris@559 1562 mdClosePort( data->port );
Chris@559 1563 connected_ = false;
Chris@559 1564
Chris@559 1565 // Shutdown the input thread.
Chris@559 1566 inputData_.doInput = false;
Chris@559 1567 pthread_join( data->thread, NULL );
Chris@559 1568 }
Chris@559 1569 }
Chris@559 1570
Chris@559 1571 RtMidiIn :: ~RtMidiIn()
Chris@559 1572 {
Chris@559 1573 // Close a connection if it exists.
Chris@559 1574 closePort();
Chris@559 1575
Chris@559 1576 // Cleanup.
Chris@559 1577 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
Chris@559 1578 delete data;
Chris@559 1579 }
Chris@559 1580
Chris@559 1581 unsigned int RtMidiIn :: getPortCount()
Chris@559 1582 {
Chris@559 1583 int nPorts = mdInit();
Chris@559 1584 if ( nPorts >= 0 ) return nPorts;
Chris@559 1585 else return 0;
Chris@559 1586 }
Chris@559 1587
Chris@559 1588 std::string RtMidiIn :: getPortName( unsigned int portNumber )
Chris@559 1589 {
Chris@559 1590 int nPorts = mdInit();
Chris@559 1591
Chris@559 1592 std::ostringstream ost;
Chris@559 1593 if ( portNumber >= nPorts ) {
Chris@559 1594 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1595 errorString_ = ost.str();
Chris@559 1596 error( RtError::INVALID_PARAMETER );
Chris@559 1597 }
Chris@559 1598
Chris@559 1599 std::string stringName = std::string( mdGetName( portNumber ) );
Chris@559 1600 return stringName;
Chris@559 1601 }
Chris@559 1602
Chris@559 1603 //*********************************************************************//
Chris@559 1604 // API: IRIX MD
Chris@559 1605 // Class Definitions: RtMidiOut
Chris@559 1606 //*********************************************************************//
Chris@559 1607
Chris@559 1608 unsigned int RtMidiOut :: getPortCount()
Chris@559 1609 {
Chris@559 1610 int nPorts = mdInit();
Chris@559 1611 if ( nPorts >= 0 ) return nPorts;
Chris@559 1612 else return 0;
Chris@559 1613 }
Chris@559 1614
Chris@559 1615 std::string RtMidiOut :: getPortName( unsigned int portNumber )
Chris@559 1616 {
Chris@559 1617 int nPorts = mdInit();
Chris@559 1618
Chris@559 1619 std::ostringstream ost;
Chris@559 1620 if ( portNumber >= nPorts ) {
Chris@559 1621 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1622 errorString_ = ost.str();
Chris@559 1623 error( RtError::INVALID_PARAMETER );
Chris@559 1624 }
Chris@559 1625
Chris@559 1626 std::string stringName = std::string( mdGetName( portNumber ) );
Chris@559 1627 return stringName;
Chris@559 1628 }
Chris@559 1629
Chris@565 1630 void RtMidiOut :: initialize( const std::string& /*clientName*/ )
Chris@559 1631 {
Chris@559 1632 // Initialize the Irix MIDI system. At the moment, we will not
Chris@559 1633 // worry about a return value of zero (ports) because there is a
Chris@559 1634 // chance the user could plug something in after instantiation.
Chris@559 1635 int nPorts = mdInit();
Chris@559 1636
Chris@559 1637 // Create our api-specific connection information.
Chris@559 1638 IrixMidiData *data = (IrixMidiData *) new IrixMidiData;
Chris@559 1639 apiData_ = (void *) data;
Chris@559 1640 }
Chris@559 1641
Chris@565 1642 void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@559 1643 {
Chris@559 1644 if ( connected_ ) {
Chris@559 1645 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
Chris@559 1646 error( RtError::WARNING );
Chris@559 1647 return;
Chris@559 1648 }
Chris@559 1649
Chris@559 1650 int nPorts = mdInit();
Chris@559 1651 if (nPorts < 1) {
Chris@559 1652 errorString_ = "RtMidiOut::openPort: no Irix MIDI output sources found!";
Chris@559 1653 error( RtError::NO_DEVICES_FOUND );
Chris@559 1654 }
Chris@559 1655
Chris@559 1656 std::ostringstream ost;
Chris@559 1657 if ( portNumber >= nPorts ) {
Chris@559 1658 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1659 errorString_ = ost.str();
Chris@559 1660 error( RtError::INVALID_PARAMETER );
Chris@559 1661 }
Chris@559 1662
Chris@559 1663 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
Chris@559 1664 data->port = mdOpenOutPort( mdGetName(portNumber) );
Chris@559 1665 if ( data->port == NULL ) {
Chris@559 1666 ost << "RtMidiOut::openPort: Irix error opening the port (" << portNumber << ").";
Chris@559 1667 errorString_ = ost.str();
Chris@559 1668 error( RtError::DRIVER_ERROR );
Chris@559 1669 }
Chris@559 1670 mdSetStampMode(data->port, MD_NOSTAMP);
Chris@559 1671
Chris@559 1672 connected_ = true;
Chris@559 1673 }
Chris@559 1674
Chris@559 1675 void RtMidiOut :: closePort( void )
Chris@559 1676 {
Chris@559 1677 if ( connected_ ) {
Chris@559 1678 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
Chris@559 1679 mdClosePort( data->port );
Chris@559 1680 connected_ = false;
Chris@559 1681 }
Chris@559 1682 }
Chris@559 1683
Chris@559 1684 void RtMidiOut :: openVirtualPort( std::string portName )
Chris@559 1685 {
Chris@559 1686 // This function cannot be implemented for the Irix MIDI API.
Chris@559 1687 errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!";
Chris@559 1688 error( RtError::WARNING );
Chris@559 1689 }
Chris@559 1690
Chris@559 1691 RtMidiOut :: ~RtMidiOut()
Chris@559 1692 {
Chris@559 1693 // Close a connection if it exists.
Chris@559 1694 closePort();
Chris@559 1695
Chris@559 1696 // Cleanup.
Chris@559 1697 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
Chris@559 1698 delete data;
Chris@559 1699 }
Chris@559 1700
Chris@559 1701 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
Chris@559 1702 {
Chris@559 1703 int result;
Chris@559 1704 MDevent event;
Chris@559 1705 IrixMidiData *data = static_cast<IrixMidiData *> (apiData_);
Chris@559 1706 char *buffer = 0;
Chris@559 1707
Chris@559 1708 unsigned int nBytes = message->size();
Chris@559 1709 if ( nBytes == 0 ) return;
Chris@559 1710 event.stamp = 0;
Chris@559 1711 if ( message->at(0) == 0xF0 ) {
Chris@559 1712 if ( nBytes < 3 ) return; // check for bogus sysex
Chris@559 1713 event.msg[0] = 0xF0;
Chris@559 1714 event.msglen = nBytes;
Chris@559 1715 buffer = (char *) malloc( nBytes );
Chris@559 1716 for ( int i=0; i<nBytes; i++ ) buffer[i] = message->at(i);
Chris@559 1717 event.sysexmsg = buffer;
Chris@559 1718 }
Chris@559 1719 else {
Chris@559 1720 for ( int i=0; i<nBytes; i++ )
Chris@559 1721 event.msg[i] = message->at(i);
Chris@559 1722 }
Chris@559 1723
Chris@559 1724 // Send the event.
Chris@559 1725 result = mdSend( data->port, &event, 1 );
Chris@559 1726 if ( buffer ) free( buffer );
Chris@559 1727 if ( result < 1 ) {
Chris@559 1728 errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!";
Chris@559 1729 error( RtError::WARNING );
Chris@559 1730 return;
Chris@559 1731 }
Chris@559 1732 }
Chris@559 1733
Chris@559 1734 #endif // __IRIX_MD__
Chris@559 1735
Chris@559 1736 //*********************************************************************//
Chris@559 1737 // API: Windows Multimedia Library (MM)
Chris@559 1738 //*********************************************************************//
Chris@559 1739
Chris@559 1740 // API information deciphered from:
Chris@559 1741 // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
Chris@559 1742
Chris@559 1743 // Thanks to Jean-Baptiste Berruchon for the sysex code.
Chris@559 1744
Chris@559 1745 #if defined(__WINDOWS_MM__)
Chris@559 1746
Chris@559 1747 // The Windows MM API is based on the use of a callback function for
Chris@559 1748 // MIDI input. We convert the system specific time stamps to delta
Chris@559 1749 // time values.
Chris@559 1750
Chris@559 1751 // Windows MM MIDI header files.
Chris@559 1752 #include <windows.h>
Chris@559 1753 #include <mmsystem.h>
Chris@559 1754
Chris@559 1755 // A structure to hold variables related to the CoreMIDI API
Chris@559 1756 // implementation.
Chris@559 1757 struct WinMidiData {
Chris@559 1758 HMIDIIN inHandle; // Handle to Midi Input Device
Chris@559 1759 HMIDIOUT outHandle; // Handle to Midi Output Device
Chris@559 1760 DWORD lastTime;
Chris@559 1761 RtMidiIn::MidiMessage message;
Chris@559 1762 LPMIDIHDR sysexBuffer;
Chris@559 1763 };
Chris@559 1764
Chris@559 1765 #define RT_SYSEX_BUFFER_SIZE 1024
Chris@559 1766
Chris@559 1767 //*********************************************************************//
Chris@559 1768 // API: Windows MM
Chris@559 1769 // Class Definitions: RtMidiIn
Chris@559 1770 //*********************************************************************//
Chris@559 1771
Chris@559 1772 static void CALLBACK midiInputCallback( HMIDIOUT hmin,
Chris@559 1773 UINT inputStatus,
Chris@559 1774 DWORD instancePtr,
Chris@559 1775 DWORD midiMessage,
Chris@559 1776 DWORD timestamp )
Chris@559 1777 {
Chris@559 1778 if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA ) return;
Chris@559 1779
Chris@559 1780 //RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (instancePtr);
Chris@559 1781 RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr;
Chris@559 1782 WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);
Chris@559 1783
Chris@559 1784 // Calculate time stamp.
Chris@559 1785 apiData->message.timeStamp = 0.0;
Chris@559 1786 if ( data->firstMessage == true ) data->firstMessage = false;
Chris@559 1787 else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;
Chris@559 1788 apiData->lastTime = timestamp;
Chris@559 1789
Chris@559 1790 if ( inputStatus == MIM_DATA ) { // Channel or system message
Chris@559 1791
Chris@559 1792 // Make sure the first byte is a status byte.
Chris@559 1793 unsigned char status = (unsigned char) (midiMessage & 0x000000FF);
Chris@559 1794 if ( !(status & 0x80) ) return;
Chris@559 1795
Chris@559 1796 // Determine the number of bytes in the MIDI message.
Chris@559 1797 unsigned short nBytes = 1;
Chris@559 1798 if ( status < 0xC0 ) nBytes = 3;
Chris@559 1799 else if ( status < 0xE0 ) nBytes = 2;
Chris@559 1800 else if ( status < 0xF0 ) nBytes = 3;
Chris@559 1801 else if ( status < 0xF3 ) {
Chris@559 1802 // A MIDI time code message and we're ignoring it.
Chris@559 1803 if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return;
Chris@559 1804 nBytes = 3;
Chris@559 1805 }
Chris@559 1806 else if ( status == 0xF3 ) nBytes = 2;
Chris@559 1807 else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {
Chris@559 1808 // A MIDI timing tick message and we're ignoring it.
Chris@559 1809 return;
Chris@559 1810 }
Chris@559 1811 else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
Chris@559 1812 // A MIDI active sensing message and we're ignoring it.
Chris@559 1813 return;
Chris@559 1814 }
Chris@559 1815
Chris@559 1816 // Copy bytes to our MIDI message.
Chris@559 1817 unsigned char *ptr = (unsigned char *) &midiMessage;
Chris@559 1818 for ( int i=0; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ );
Chris@559 1819 }
Chris@565 1820 else { // Sysex message ( MIM_LONGDATA )
Chris@565 1821 MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
Chris@565 1822 if ( !( data->ignoreFlags & 0x01 ) ) {
Chris@565 1823 // Sysex message and we're not ignoring it
Chris@565 1824 for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ )
Chris@565 1825 apiData->message.bytes.push_back( sysex->lpData[i] );
Chris@565 1826 }
Chris@559 1827
Chris@565 1828 // The WinMM API requires that the sysex buffer be requeued after
Chris@565 1829 // input of each sysex message. Even if we are ignoring sysex
Chris@565 1830 // messages, we still need to requeue the buffer in case the user
Chris@565 1831 // decides to not ignore sysex messages in the future. However,
Chris@565 1832 // it seems that WinMM calls this function with an empty sysex
Chris@565 1833 // buffer when an application closes and in this case, we should
Chris@565 1834 // avoid requeueing it, else the computer suddenly reboots after
Chris@565 1835 // one or two minutes.
Chris@559 1836 if ( apiData->sysexBuffer->dwBytesRecorded > 0 ) {
Chris@565 1837 //if ( sysex->dwBytesRecorded > 0 ) {
Chris@559 1838 MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) );
Chris@559 1839 if ( result != MMSYSERR_NOERROR )
Chris@559 1840 std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
Chris@565 1841
Chris@565 1842 if ( data->ignoreFlags & 0x01 ) return;
Chris@559 1843 }
Chris@565 1844 else return;
Chris@559 1845 }
Chris@559 1846
Chris@559 1847 if ( data->usingCallback ) {
Chris@559 1848 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
Chris@559 1849 callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );
Chris@559 1850 }
Chris@559 1851 else {
Chris@559 1852 // As long as we haven't reached our queue size limit, push the message.
Chris@559 1853 if ( data->queueLimit > data->queue.size() )
Chris@559 1854 data->queue.push( apiData->message );
Chris@559 1855 else
Chris@559 1856 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
Chris@559 1857 }
Chris@559 1858
Chris@565 1859 // Clear the vector for the next input message.
Chris@559 1860 apiData->message.bytes.clear();
Chris@559 1861 }
Chris@559 1862
Chris@565 1863 void RtMidiIn :: initialize( const std::string& /*clientName*/ )
Chris@559 1864 {
Chris@559 1865 // We'll issue a warning here if no devices are available but not
Chris@559 1866 // throw an error since the user can plugin something later.
Chris@559 1867 unsigned int nDevices = midiInGetNumDevs();
Chris@559 1868 if ( nDevices == 0 ) {
Chris@559 1869 errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available.";
Chris@559 1870 error( RtError::WARNING );
Chris@559 1871 }
Chris@559 1872
Chris@559 1873 // Save our api-specific connection information.
Chris@559 1874 WinMidiData *data = (WinMidiData *) new WinMidiData;
Chris@559 1875 apiData_ = (void *) data;
Chris@559 1876 inputData_.apiData = (void *) data;
Chris@559 1877 data->message.bytes.clear(); // needs to be empty for first input message
Chris@559 1878 }
Chris@559 1879
Chris@565 1880 void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@559 1881 {
Chris@559 1882 if ( connected_ ) {
Chris@559 1883 errorString_ = "RtMidiIn::openPort: a valid connection already exists!";
Chris@559 1884 error( RtError::WARNING );
Chris@559 1885 return;
Chris@559 1886 }
Chris@559 1887
Chris@559 1888 unsigned int nDevices = midiInGetNumDevs();
Chris@559 1889 if (nDevices == 0) {
Chris@559 1890 errorString_ = "RtMidiIn::openPort: no MIDI input sources found!";
Chris@559 1891 error( RtError::NO_DEVICES_FOUND );
Chris@559 1892 }
Chris@559 1893
Chris@559 1894 std::ostringstream ost;
Chris@559 1895 if ( portNumber >= nDevices ) {
Chris@559 1896 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1897 errorString_ = ost.str();
Chris@559 1898 error( RtError::INVALID_PARAMETER );
Chris@559 1899 }
Chris@559 1900
Chris@559 1901 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 1902 MMRESULT result = midiInOpen( &data->inHandle,
Chris@559 1903 portNumber,
Chris@559 1904 (DWORD)&midiInputCallback,
Chris@559 1905 (DWORD)&inputData_,
Chris@559 1906 CALLBACK_FUNCTION );
Chris@559 1907 if ( result != MMSYSERR_NOERROR ) {
Chris@559 1908 errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port.";
Chris@559 1909 error( RtError::DRIVER_ERROR );
Chris@559 1910 }
Chris@559 1911
Chris@559 1912 // Allocate and init the sysex buffer.
Chris@559 1913 data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
Chris@565 1914 data->sysexBuffer->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
Chris@565 1915 data->sysexBuffer->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
Chris@559 1916 data->sysexBuffer->dwFlags = 0;
Chris@559 1917
Chris@559 1918 result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) );
Chris@559 1919 if ( result != MMSYSERR_NOERROR ) {
Chris@559 1920 midiInClose( data->inHandle );
Chris@559 1921 errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
Chris@559 1922 error( RtError::DRIVER_ERROR );
Chris@559 1923 }
Chris@559 1924
Chris@559 1925 // Register the buffer.
Chris@559 1926 result = midiInAddBuffer( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) );
Chris@559 1927 if ( result != MMSYSERR_NOERROR ) {
Chris@559 1928 midiInClose( data->inHandle );
Chris@559 1929 errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (AddBuffer).";
Chris@559 1930 error( RtError::DRIVER_ERROR );
Chris@559 1931 }
Chris@559 1932
Chris@559 1933 result = midiInStart( data->inHandle );
Chris@559 1934 if ( result != MMSYSERR_NOERROR ) {
Chris@559 1935 midiInClose( data->inHandle );
Chris@559 1936 errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port.";
Chris@559 1937 error( RtError::DRIVER_ERROR );
Chris@559 1938 }
Chris@559 1939
Chris@559 1940 connected_ = true;
Chris@559 1941 }
Chris@559 1942
Chris@559 1943 void RtMidiIn :: openVirtualPort( std::string portName )
Chris@559 1944 {
Chris@559 1945 // This function cannot be implemented for the Windows MM MIDI API.
Chris@559 1946 errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
Chris@559 1947 error( RtError::WARNING );
Chris@559 1948 }
Chris@559 1949
Chris@559 1950 void RtMidiIn :: closePort( void )
Chris@559 1951 {
Chris@559 1952 if ( connected_ ) {
Chris@559 1953 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 1954 midiInReset( data->inHandle );
Chris@559 1955 midiInStop( data->inHandle );
Chris@559 1956
Chris@559 1957 int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer, sizeof(MIDIHDR));
Chris@559 1958 delete [] data->sysexBuffer->lpData;
Chris@559 1959 delete [] data->sysexBuffer;
Chris@559 1960 if ( result != MMSYSERR_NOERROR ) {
Chris@559 1961 midiInClose( data->inHandle );
Chris@559 1962 errorString_ = "RtMidiIn::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
Chris@559 1963 error( RtError::DRIVER_ERROR );
Chris@559 1964 }
Chris@559 1965
Chris@559 1966 midiInClose( data->inHandle );
Chris@559 1967 connected_ = false;
Chris@559 1968 }
Chris@559 1969 }
Chris@559 1970
Chris@559 1971 RtMidiIn :: ~RtMidiIn()
Chris@559 1972 {
Chris@559 1973 // Close a connection if it exists.
Chris@559 1974 closePort();
Chris@559 1975
Chris@559 1976 // Cleanup.
Chris@559 1977 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 1978 delete data;
Chris@559 1979 }
Chris@559 1980
Chris@559 1981 unsigned int RtMidiIn :: getPortCount()
Chris@559 1982 {
Chris@559 1983 return midiInGetNumDevs();
Chris@559 1984 }
Chris@559 1985
Chris@559 1986 std::string RtMidiIn :: getPortName( unsigned int portNumber )
Chris@559 1987 {
Chris@559 1988 unsigned int nDevices = midiInGetNumDevs();
Chris@559 1989 if ( portNumber >= nDevices ) {
Chris@559 1990 std::ostringstream ost;
Chris@559 1991 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 1992 errorString_ = ost.str();
Chris@559 1993 error( RtError::INVALID_PARAMETER );
Chris@559 1994 }
Chris@559 1995
Chris@559 1996 MIDIINCAPS deviceCaps;
Chris@559 1997 midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
Chris@559 1998
Chris@559 1999 // For some reason, we need to copy character by character with
Chris@559 2000 // UNICODE (thanks to Eduardo Coutinho!).
Chris@559 2001 //std::string stringName = std::string( deviceCaps.szPname );
Chris@559 2002 char nameString[MAXPNAMELEN];
Chris@559 2003 for( int i=0; i<MAXPNAMELEN; i++ )
Chris@559 2004 nameString[i] = (char)( deviceCaps.szPname[i] );
Chris@559 2005
Chris@559 2006 std::string stringName( nameString );
Chris@559 2007 return stringName;
Chris@559 2008 }
Chris@559 2009
Chris@559 2010 //*********************************************************************//
Chris@559 2011 // API: Windows MM
Chris@559 2012 // Class Definitions: RtMidiOut
Chris@559 2013 //*********************************************************************//
Chris@559 2014
Chris@559 2015 unsigned int RtMidiOut :: getPortCount()
Chris@559 2016 {
Chris@559 2017 return midiOutGetNumDevs();
Chris@559 2018 }
Chris@559 2019
Chris@559 2020 std::string RtMidiOut :: getPortName( unsigned int portNumber )
Chris@559 2021 {
Chris@559 2022 unsigned int nDevices = midiOutGetNumDevs();
Chris@559 2023 if ( portNumber >= nDevices ) {
Chris@559 2024 std::ostringstream ost;
Chris@559 2025 ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 2026 errorString_ = ost.str();
Chris@559 2027 error( RtError::INVALID_PARAMETER );
Chris@559 2028 }
Chris@559 2029
Chris@559 2030 MIDIOUTCAPS deviceCaps;
Chris@559 2031 midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));
Chris@559 2032
Chris@559 2033 // For some reason, we need to copy character by character with
Chris@559 2034 // UNICODE (thanks to Eduardo Coutinho!).
Chris@559 2035 //std::string stringName = std::string( deviceCaps.szPname );
Chris@559 2036 char nameString[MAXPNAMELEN];
Chris@559 2037 for( int i=0; i<MAXPNAMELEN; i++ )
Chris@559 2038 nameString[i] = (char)( deviceCaps.szPname[i] );
Chris@559 2039
Chris@559 2040 std::string stringName( nameString );
Chris@559 2041 return stringName;
Chris@559 2042 }
Chris@559 2043
Chris@565 2044 void RtMidiOut :: initialize( const std::string& /*clientName*/ )
Chris@559 2045 {
Chris@559 2046 // We'll issue a warning here if no devices are available but not
Chris@559 2047 // throw an error since the user can plug something in later.
Chris@559 2048 unsigned int nDevices = midiOutGetNumDevs();
Chris@559 2049 if ( nDevices == 0 ) {
Chris@559 2050 errorString_ = "RtMidiOut::initialize: no MIDI output devices currently available.";
Chris@559 2051 error( RtError::WARNING );
Chris@559 2052 }
Chris@559 2053
Chris@559 2054 // Save our api-specific connection information.
Chris@559 2055 WinMidiData *data = (WinMidiData *) new WinMidiData;
Chris@559 2056 apiData_ = (void *) data;
Chris@559 2057 }
Chris@559 2058
Chris@565 2059 void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@559 2060 {
Chris@559 2061 if ( connected_ ) {
Chris@559 2062 errorString_ = "RtMidiOut::openPort: a valid connection already exists!";
Chris@559 2063 error( RtError::WARNING );
Chris@559 2064 return;
Chris@559 2065 }
Chris@559 2066
Chris@559 2067 unsigned int nDevices = midiOutGetNumDevs();
Chris@559 2068 if (nDevices < 1) {
Chris@559 2069 errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!";
Chris@559 2070 error( RtError::NO_DEVICES_FOUND );
Chris@559 2071 }
Chris@559 2072
Chris@559 2073 std::ostringstream ost;
Chris@559 2074 if ( portNumber >= nDevices ) {
Chris@559 2075 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
Chris@559 2076 errorString_ = ost.str();
Chris@559 2077 error( RtError::INVALID_PARAMETER );
Chris@559 2078 }
Chris@559 2079
Chris@559 2080 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2081 MMRESULT result = midiOutOpen( &data->outHandle,
Chris@559 2082 portNumber,
Chris@559 2083 (DWORD)NULL,
Chris@559 2084 (DWORD)NULL,
Chris@559 2085 CALLBACK_NULL );
Chris@559 2086 if ( result != MMSYSERR_NOERROR ) {
Chris@559 2087 errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port.";
Chris@559 2088 error( RtError::DRIVER_ERROR );
Chris@559 2089 }
Chris@559 2090
Chris@559 2091 connected_ = true;
Chris@559 2092 }
Chris@559 2093
Chris@559 2094 void RtMidiOut :: closePort( void )
Chris@559 2095 {
Chris@559 2096 if ( connected_ ) {
Chris@559 2097 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2098 midiOutReset( data->outHandle );
Chris@559 2099 midiOutClose( data->outHandle );
Chris@559 2100 connected_ = false;
Chris@559 2101 }
Chris@559 2102 }
Chris@559 2103
Chris@559 2104 void RtMidiOut :: openVirtualPort( std::string portName )
Chris@559 2105 {
Chris@559 2106 // This function cannot be implemented for the Windows MM MIDI API.
Chris@559 2107 errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
Chris@559 2108 error( RtError::WARNING );
Chris@559 2109 }
Chris@559 2110
Chris@559 2111 RtMidiOut :: ~RtMidiOut()
Chris@559 2112 {
Chris@559 2113 // Close a connection if it exists.
Chris@559 2114 closePort();
Chris@559 2115
Chris@559 2116 // Cleanup.
Chris@559 2117 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2118 delete data;
Chris@559 2119 }
Chris@559 2120
Chris@559 2121 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
Chris@559 2122 {
Chris@559 2123 unsigned int nBytes = message->size();
Chris@559 2124 if ( nBytes == 0 ) {
Chris@559 2125 errorString_ = "RtMidiOut::sendMessage: message argument is empty!";
Chris@559 2126 error( RtError::WARNING );
Chris@559 2127 return;
Chris@559 2128 }
Chris@559 2129
Chris@559 2130 MMRESULT result;
Chris@559 2131 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
Chris@559 2132 if ( message->at(0) == 0xF0 ) { // Sysex message
Chris@559 2133
Chris@559 2134 // Allocate buffer for sysex data.
Chris@559 2135 char *buffer = (char *) malloc( nBytes );
Chris@559 2136 if ( buffer == NULL ) {
Chris@559 2137 errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!";
Chris@559 2138 error( RtError::MEMORY_ERROR );
Chris@559 2139 }
Chris@559 2140
Chris@559 2141 // Copy data to buffer.
Chris@559 2142 for ( unsigned int i=0; i<nBytes; i++ ) buffer[i] = message->at(i);
Chris@559 2143
Chris@559 2144 // Create and prepare MIDIHDR structure.
Chris@559 2145 MIDIHDR sysex;
Chris@559 2146 sysex.lpData = (LPSTR) buffer;
Chris@559 2147 sysex.dwBufferLength = nBytes;
Chris@559 2148 sysex.dwFlags = 0;
Chris@559 2149 result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) );
Chris@559 2150 if ( result != MMSYSERR_NOERROR ) {
Chris@559 2151 free( buffer );
Chris@559 2152 errorString_ = "RtMidiOut::sendMessage: error preparing sysex header.";
Chris@559 2153 error( RtError::DRIVER_ERROR );
Chris@559 2154 }
Chris@559 2155
Chris@559 2156 // Send the message.
Chris@559 2157 result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) );
Chris@559 2158 if ( result != MMSYSERR_NOERROR ) {
Chris@559 2159 free( buffer );
Chris@559 2160 errorString_ = "RtMidiOut::sendMessage: error sending sysex message.";
Chris@559 2161 error( RtError::DRIVER_ERROR );
Chris@559 2162 }
Chris@559 2163
Chris@559 2164 // Unprepare the buffer and MIDIHDR.
Chris@559 2165 while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 );
Chris@559 2166 free( buffer );
Chris@559 2167
Chris@559 2168 }
Chris@559 2169 else { // Channel or system message.
Chris@559 2170
Chris@559 2171 // Make sure the message size isn't too big.
Chris@559 2172 if ( nBytes > 3 ) {
Chris@559 2173 errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!";
Chris@559 2174 error( RtError::WARNING );
Chris@559 2175 return;
Chris@559 2176 }
Chris@559 2177
Chris@559 2178 // Pack MIDI bytes into double word.
Chris@559 2179 DWORD packet;
Chris@559 2180 unsigned char *ptr = (unsigned char *) &packet;
Chris@559 2181 for ( unsigned int i=0; i<nBytes; i++ ) {
Chris@559 2182 *ptr = message->at(i);
Chris@559 2183 ptr++;
Chris@559 2184 }
Chris@559 2185
Chris@559 2186 // Send the message immediately.
Chris@559 2187 result = midiOutShortMsg( data->outHandle, packet );
Chris@559 2188 if ( result != MMSYSERR_NOERROR ) {
Chris@559 2189 errorString_ = "RtMidiOut::sendMessage: error sending MIDI message.";
Chris@559 2190 error( RtError::DRIVER_ERROR );
Chris@559 2191 }
Chris@559 2192 }
Chris@559 2193 }
Chris@559 2194
Chris@559 2195 #endif // __WINDOWS_MM__
Chris@609 2196
Chris@609 2197 #ifdef __RTMIDI_DUMMY_ONLY__
Chris@609 2198
Chris@609 2199 void RtMidiIn :: initialize( const std::string& /*clientName*/ )
Chris@609 2200 {
Chris@609 2201 }
Chris@609 2202
Chris@609 2203 void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@609 2204 {
Chris@609 2205 }
Chris@609 2206
Chris@609 2207 void RtMidiIn :: openVirtualPort( std::string portName )
Chris@609 2208 {
Chris@609 2209 }
Chris@609 2210
Chris@609 2211 void RtMidiIn :: closePort( void )
Chris@609 2212 {
Chris@609 2213 }
Chris@609 2214
Chris@609 2215 RtMidiIn :: ~RtMidiIn()
Chris@609 2216 {
Chris@609 2217 }
Chris@609 2218
Chris@609 2219 unsigned int RtMidiIn :: getPortCount()
Chris@609 2220 {
Chris@609 2221 return 0;
Chris@609 2222 }
Chris@609 2223
Chris@609 2224 std::string RtMidiIn :: getPortName( unsigned int portNumber )
Chris@609 2225 {
Chris@609 2226 return "";
Chris@609 2227 }
Chris@609 2228
Chris@609 2229 unsigned int RtMidiOut :: getPortCount()
Chris@609 2230 {
Chris@609 2231 return 0;
Chris@609 2232 }
Chris@609 2233
Chris@609 2234 std::string RtMidiOut :: getPortName( unsigned int portNumber )
Chris@609 2235 {
Chris@609 2236 return "";
Chris@609 2237 }
Chris@609 2238
Chris@609 2239 void RtMidiOut :: initialize( const std::string& /*clientName*/ )
Chris@609 2240 {
Chris@609 2241 }
Chris@609 2242
Chris@609 2243 void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ )
Chris@609 2244 {
Chris@609 2245 }
Chris@609 2246
Chris@609 2247 void RtMidiOut :: closePort( void )
Chris@609 2248 {
Chris@609 2249 }
Chris@609 2250
Chris@609 2251 void RtMidiOut :: openVirtualPort( std::string portName )
Chris@609 2252 {
Chris@609 2253 }
Chris@609 2254
Chris@609 2255 RtMidiOut :: ~RtMidiOut()
Chris@609 2256 {
Chris@609 2257 }
Chris@609 2258
Chris@609 2259 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
Chris@609 2260 {
Chris@609 2261 }
Chris@609 2262
Chris@609 2263 #endif __RTMIDI_DUMMY_ONLY__
Chris@609 2264