
Home Information Classes Download Usage Mail List Requirements Links Tutorial
00001 /**********************************************************************/ 00010 /**********************************************************************/ 00011 00012 #include "RtMidi.h" 00013 #include <sstream> 00014 00015 RtMidi :: RtMidi() 00016 : apiData_( 0 ), connected_( false ) 00017 { 00018 } 00019 00020 void RtMidi :: error( RtError::Type type ) 00021 { 00022 if (type == RtError::WARNING) { 00023 std::cerr << '\n' << errorString_ << "\n\n"; 00024 } 00025 else if (type == RtError::DEBUG_WARNING) { 00026 #if defined(__RTMIDI_DEBUG__) 00027 std::cerr << '\n' << errorString_ << "\n\n"; 00028 #endif 00029 } 00030 else { 00031 std::cerr << '\n' << errorString_ << "\n\n"; 00032 throw RtError( errorString_, type ); 00033 } 00034 } 00035 00036 RtMidiIn :: RtMidiIn() : RtMidi() 00037 { 00038 } 00039 00040 RtMidiIn :: RtMidiIn( unsigned int portNumber, RtMidiCallback callback, void *userData ) : RtMidi() 00041 00042 { 00043 // Attempt to make the connection. 00044 this->openPort( portNumber, callback, userData ); 00045 } 00046 00047 RtMidiIn :: ~RtMidiIn() 00048 { 00049 closePort(); 00050 } 00051 00052 RtMidiOut :: RtMidiOut() : RtMidi() 00053 { 00054 } 00055 00056 RtMidiOut :: RtMidiOut( unsigned int portNumber ) : RtMidi() 00057 { 00058 // Attempt to make the connection. 00059 this->openPort( portNumber ); 00060 } 00061 00062 RtMidiOut :: ~RtMidiOut() 00063 { 00064 closePort(); 00065 } 00066 00067 00068 #if defined(__MACOSX_CORE__) 00069 00070 // The CoreMIDI API is based on the use of a callback function for 00071 // MIDI input. We convert the system specific time stamps to delta 00072 // time values. 00073 00074 // OS-X CoreMIDI header files. 00075 #include <CoreMIDI/CoreMIDI.h> 00076 #include <CoreAudio/HostTime.h> 00077 00078 // A structure to hold variables related to the CoreMIDI API 00079 // implementation. 00080 struct CoreMidiData { 00081 MIDIClientRef client; 00082 MIDIPortRef port; 00083 MIDIEndpointRef destinationId; 00084 }; 00085 00086 void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) 00087 { 00088 RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (procRef); 00089 00090 bool continueSysex = false; 00091 unsigned char status; 00092 unsigned short nBytes, iByte, size; 00093 unsigned long long time; 00094 RtMidiIn::MidiMessage message; 00095 const MIDIPacket *packet = &list->packet[0]; 00096 for ( unsigned int i=0; i<list->numPackets; ++i ) { 00097 00098 // My interpretation of the CoreMIDI documentation: all message 00099 // types, except sysex, are complete within a packet and there may 00100 // be several of them in a single packet. Sysex messages can be 00101 // broken across multiple packets but are bundled alone within a 00102 // packet. I'm assuming that sysex messages, if segmented, must 00103 // be complete within the same MIDIPacketList. 00104 00105 nBytes = packet->length; 00106 if ( nBytes == 0 ) continue; 00107 00108 // Calculate time stamp. 00109 message.timeStamp = 0.0; 00110 time = packet->timeStamp; 00111 time -= data->lastTime; 00112 data->lastTime = packet->timeStamp; 00113 time = AudioConvertHostTimeToNanos( time ); 00114 message.timeStamp = time * 0.000000001; 00115 00116 iByte = 0; 00117 if ( continueSysex ) { 00118 // We have a continuing, segmented sysex message. 00119 if ( !(data->ignoreFlags & 0x01) ) { 00120 // If we're not ignoring sysex messages, copy the entire packet. 00121 message.bytes.assign( packet->data, &packet->data[nBytes] ); 00122 } 00123 if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; 00124 if ( !continueSysex ) { 00125 // If not a continuing sysex message, invoke the user callback function or queue the message. 00126 if ( data->usingCallback && message.bytes.size() > 0 ) { 00127 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 00128 callback( message.timeStamp, &message.bytes, data->userData ); 00129 } 00130 else { 00131 // As long as we haven't reached our queue size limit, push the message. 00132 if ( data->queueLimit > data->queue.size() ) 00133 data->queue.push( message ); 00134 else 00135 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; 00136 } 00137 message.bytes.clear(); 00138 } 00139 } 00140 else { 00141 while ( iByte < nBytes ) { 00142 size = 0; 00143 // We are expecting that the next byte in the packet is a status byte. 00144 status = packet->data[iByte]; 00145 if ( !(status & 0x80) ) break; 00146 // Determine the number of bytes in the MIDI message. 00147 if ( status < 0xC0 ) size = 3; 00148 else if ( status < 0xE0 ) size = 2; 00149 else if ( status < 0xF0 ) size = 3; 00150 else if ( status == 0xF0 ) { 00151 // A MIDI sysex 00152 if ( data->ignoreFlags & 0x01 ) { 00153 size = 0; 00154 iByte = nBytes; 00155 } 00156 else size = nBytes - iByte; 00157 if ( packet->data[nBytes] == 0xF7 ) continueSysex = false; 00158 } 00159 else if ( status < 0xF3 ) { 00160 if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { 00161 // A MIDI time code message and we're ignoring it. 00162 size = 0; 00163 iByte += 3; 00164 } 00165 else size = 3; 00166 } 00167 else if ( status == 0xF3 ) size = 2; 00168 else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { 00169 // A MIDI active sensing message and we're ignoring it. 00170 size = 0; 00171 iByte += 1; 00172 } 00173 else size = 1; 00174 00175 // Copy the MIDI data to our vector. 00176 if ( size ) { 00177 message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); 00178 if ( !continueSysex ) { 00179 // If not a continuing sysex message, invoke the user callback function or queue the message. 00180 if ( data->usingCallback ) { 00181 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 00182 callback( message.timeStamp, &message.bytes, data->userData ); 00183 } 00184 else { 00185 // As long as we haven't reached our queue size limit, push the message. 00186 if ( data->queueLimit > data->queue.size() ) 00187 data->queue.push( message ); 00188 else 00189 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; 00190 } 00191 message.bytes.clear(); 00192 } 00193 iByte += size; 00194 } 00195 } 00196 } 00197 packet = MIDIPacketNext(packet); 00198 } 00199 } 00200 00201 void RtMidiIn :: openPort( unsigned int portNumber, RtMidiCallback callback, void *userData ) 00202 { 00203 if ( connected_ ) { 00204 errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; 00205 error( RtError::WARNING ); 00206 return; 00207 } 00208 00209 unsigned int nSrc = MIDIGetNumberOfSources(); 00210 if (nSrc < 1) { 00211 errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; 00212 error( RtError::NO_DEVICES_FOUND ); 00213 } 00214 00215 std::ostringstream ost; 00216 if ( portNumber >= nSrc ) { 00217 ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 00218 errorString_ = ost.str(); 00219 error( RtError::INVALID_PARAMETER ); 00220 } 00221 00222 MIDIClientRef client; 00223 OSStatus result = MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL, NULL, &client ); 00224 if ( result != noErr ) { 00225 errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI client object."; 00226 error( RtError::DRIVER_ERROR ); 00227 } 00228 00229 MIDIPortRef port; 00230 result = MIDIInputPortCreate( client, CFSTR("RtMidi Virtual MIDI Input Port"), midiInputCallback, (void *)&inputData_, &port ); 00231 if ( result != noErr ) { 00232 MIDIClientDispose( client ); 00233 errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; 00234 error( RtError::DRIVER_ERROR ); 00235 } 00236 00237 // Get the desired input source identifier. 00238 MIDIEndpointRef src = MIDIGetSource( portNumber ); 00239 if ( src == NULL ) { 00240 MIDIPortDispose( port ); 00241 MIDIClientDispose( client ); 00242 errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference."; 00243 error( RtError::DRIVER_ERROR ); 00244 } 00245 00246 // Make the connection. 00247 result = MIDIPortConnectSource(port, src, NULL ); 00248 if ( result != noErr ) { 00249 MIDIPortDispose( port ); 00250 MIDIClientDispose( client ); 00251 errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port."; 00252 error( RtError::DRIVER_ERROR ); 00253 } 00254 00255 // Save our api-specific connection information. 00256 CoreMidiData *data = (CoreMidiData *) new CoreMidiData; 00257 data->client = client; 00258 data->port = port; 00259 apiData_ = (void *) data; 00260 00261 if ( callback ) { 00262 inputData_.userCallback = (void *) callback; 00263 inputData_.userData = userData; 00264 inputData_.usingCallback = true; 00265 } 00266 00267 inputData_.lastTime = AudioGetCurrentHostTime(); 00268 00269 connected_ = true; 00270 } 00271 00272 void RtMidiIn :: closePort( void ) 00273 { 00274 if ( connected_ ) { 00275 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 00276 MIDIClientDispose( data->client ); 00277 MIDIPortDispose( data->port ); 00278 delete data; 00279 apiData_ = 0; 00280 } 00281 00282 connected_ = false; 00283 } 00284 00285 unsigned int RtMidiIn :: getPortCount() 00286 { 00287 return MIDIGetNumberOfSources(); 00288 } 00289 00290 std::string RtMidiIn :: getPortName( unsigned int portNumber ) 00291 { 00292 CFStringRef nameRef; 00293 MIDIEndpointRef portRef; 00294 std::ostringstream ost; 00295 char name[128]; 00296 00297 if ( portNumber >= MIDIGetNumberOfSources() ) { 00298 ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 00299 errorString_ = ost.str(); 00300 error( RtError::INVALID_PARAMETER ); 00301 } 00302 portRef = MIDIGetSource( portNumber ); 00303 00304 MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); 00305 CFStringGetCString( nameRef, name, sizeof(name), 0); 00306 CFRelease( nameRef ); 00307 std::string stringName = name; 00308 return stringName; 00309 } 00310 00311 void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize ) 00312 { 00313 inputData_.queueLimit = queueSize; 00314 } 00315 00316 void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) 00317 { 00318 inputData_.ignoreFlags = 0; 00319 if ( midiSysex ) inputData_.ignoreFlags = 0x01; 00320 if ( midiTime ) inputData_.ignoreFlags |= 0x02; 00321 if ( midiSense ) inputData_.ignoreFlags |= 0x04; 00322 } 00323 00324 double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) 00325 { 00326 message->clear(); 00327 if ( !connected_ ) { 00328 errorString_ = "RtMidiIn::getNextMessage: a port is not open!"; 00329 error( RtError::WARNING ); 00330 return 0.0; 00331 } 00332 00333 if ( inputData_.usingCallback ) { 00334 errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; 00335 error( RtError::WARNING ); 00336 return 0.0; 00337 } 00338 00339 if ( inputData_.queue.size() == 0 ) return 0.0; 00340 00341 // Copy queued message to the vector pointer argument and then "pop" it. 00342 std::vector<unsigned char> *bytes = &(inputData_.queue.front().bytes); 00343 message->assign( bytes->begin(), bytes->end() ); 00344 double deltaTime = inputData_.queue.front().timeStamp; 00345 inputData_.queue.pop(); 00346 00347 return deltaTime; 00348 } 00349 00350 unsigned int RtMidiOut :: getPortCount() 00351 { 00352 return MIDIGetNumberOfDestinations(); 00353 } 00354 00355 std::string RtMidiOut :: getPortName( unsigned int portNumber ) 00356 { 00357 CFStringRef nameRef; 00358 MIDIEndpointRef portRef; 00359 std::ostringstream ost; 00360 char name[128]; 00361 00362 if ( portNumber >= MIDIGetNumberOfDestinations() ) { 00363 ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 00364 errorString_ = ost.str(); 00365 error( RtError::INVALID_PARAMETER ); 00366 } 00367 portRef = MIDIGetDestination( portNumber ); 00368 00369 MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); 00370 CFStringGetCString( nameRef, name, sizeof(name), 0); 00371 CFRelease( nameRef ); 00372 std::string stringName = name; 00373 return stringName; 00374 } 00375 00376 void RtMidiOut :: openPort( unsigned int portNumber ) 00377 { 00378 if ( connected_ ) { 00379 errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; 00380 error( RtError::WARNING ); 00381 return; 00382 } 00383 00384 unsigned int nDest = MIDIGetNumberOfDestinations(); 00385 if (nDest < 1) { 00386 errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; 00387 error( RtError::NO_DEVICES_FOUND ); 00388 } 00389 00390 std::ostringstream ost; 00391 if ( portNumber >= nDest ) { 00392 ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 00393 errorString_ = ost.str(); 00394 error( RtError::INVALID_PARAMETER ); 00395 } 00396 00397 MIDIClientRef client; 00398 OSStatus result = MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL, NULL, &client ); 00399 if ( result != noErr ) { 00400 errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI client object."; 00401 error( RtError::DRIVER_ERROR ); 00402 } 00403 00404 MIDIPortRef port; 00405 result = MIDIOutputPortCreate( client, CFSTR("RtMidi Virtual MIDI Output Port"), &port ); 00406 if ( result != noErr ) { 00407 MIDIClientDispose( client ); 00408 errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; 00409 error( RtError::DRIVER_ERROR ); 00410 } 00411 00412 // Get the desired output port identifier. 00413 MIDIEndpointRef destinationId = MIDIGetDestination( portNumber ); 00414 if ( destinationId == NULL ) { 00415 MIDIPortDispose( port ); 00416 MIDIClientDispose( client ); 00417 errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference."; 00418 error( RtError::DRIVER_ERROR ); 00419 } 00420 00421 // Save our api-specific connection information. 00422 CoreMidiData *data = (CoreMidiData *) new CoreMidiData; 00423 data->client = client; 00424 data->port = port; 00425 data->destinationId = destinationId; 00426 apiData_ = (void *) data; 00427 00428 connected_ = true; 00429 } 00430 00431 void RtMidiOut :: closePort( void ) 00432 { 00433 if ( connected_ ) { 00434 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 00435 MIDIClientDispose( data->client ); 00436 MIDIPortDispose( data->port ); 00437 delete data; 00438 apiData_ = 0; 00439 } 00440 00441 connected_ = false; 00442 } 00443 00444 void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) 00445 { 00446 if ( !connected_ ) { 00447 errorString_ = "RtMidiOut::sendMessage: a port is not open!"; 00448 error( RtError::WARNING ); 00449 return; 00450 } 00451 00452 unsigned int nBytes = message->size(); 00453 // Pad the buffer for extra (unknown) structure data. 00454 Byte buffer[nBytes+32]; 00455 MIDIPacketList *pktlist = (MIDIPacketList *) buffer; 00456 MIDIPacket *curPacket = MIDIPacketListInit( pktlist ); 00457 00458 MIDITimeStamp timeStamp = 0; 00459 curPacket = MIDIPacketListAdd( pktlist, sizeof(buffer), curPacket, timeStamp, nBytes, &message->at(0) ); 00460 00461 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); 00462 OSStatus result = MIDISend( data->port, data->destinationId, pktlist ); 00463 if ( result != noErr ) { 00464 errorString_ = "RtMidiOut::sendMessage: error sending MIDI message."; 00465 error( RtError::WARNING ); 00466 } 00467 } 00468 00469 #endif 00470
| The Synthesis ToolKit in C++ (STK) |
| ©1995-2002 Perry R. Cook and Gary P. Scavone. All Rights Reserved. |