├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── RtMidi ├── RtError.h ├── RtMidi.cpp └── RtMidi.h ├── rtmidi_python.cpp ├── rtmidi_python.pyx ├── setup.py └── tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /cython_debug 3 | /dist 4 | 5 | /rtmidi_python.so 6 | /rtmidi_python.pyd 7 | 8 | /MANIFEST 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Guido Lorenz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE rtmidi_python.pyx 2 | include RtMidi/Rt* 3 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | rtmidi-python 2 | ============= 3 | 4 | Python wrapper for `RtMidi`_, the lightweight, cross-platform MIDI I/O 5 | library. For Linux, Mac OS X and Windows. 6 | 7 | Installation 8 | ------------ 9 | 10 | On Windows, the preferred way is to use one of the precompiled binaries 11 | available from `PyPI`_. On Linux and Mac OS X, the easiest way to install 12 | *rtmidi-python* is using pip:: 13 | 14 | pip install rtmidi-python 15 | 16 | Alternatively, you can build the module from source as follows:: 17 | 18 | python setup.py install 19 | 20 | If you want to build from the `Cython`_ source, make sure that you have a 21 | recent version of Cython (>= 0.17), and run:: 22 | 23 | python setup.py install --from-cython 24 | 25 | Usage Examples 26 | -------------- 27 | 28 | *rtmidi-python* uses the same API as `RtMidi`_, only reformatted to comply 29 | with PEP-8, and with small changes to make it a little more pythonic. 30 | 31 | Print all output ports 32 | ~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | :: 35 | 36 | import rtmidi_python as rtmidi 37 | 38 | midi_out = rtmidi.MidiOut() 39 | for port_name in midi_out.ports: 40 | print port_name 41 | 42 | Send messages 43 | ~~~~~~~~~~~~~ 44 | 45 | :: 46 | 47 | import rtmidi_python as rtmidi 48 | 49 | midi_out = rtmidi.MidiOut() 50 | midi_out.open_port(0) 51 | 52 | midi_out.send_message([0x90, 48, 100]) # Note on 53 | midi_out.send_message([0x80, 48, 100]) # Note off 54 | 55 | Get incoming messages by polling 56 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 57 | 58 | :: 59 | 60 | import rtmidi_python as rtmidi 61 | 62 | midi_in = rtmidi.MidiIn() 63 | midi_in.open_port(0) 64 | 65 | while True: 66 | message, delta_time = midi_in.get_message() 67 | if message: 68 | print message, delta_time 69 | 70 | Note that the signature of ``get_message()`` differs from the original 71 | `RtMidi`_ API: It returns a tuple instead of using a return parameter. 72 | 73 | Get incoming messages using a callback 74 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 75 | 76 | :: 77 | 78 | import rtmidi_python as rtmidi 79 | 80 | def callback(message, time_stamp): 81 | print message, time_stamp 82 | 83 | midi_in = rtmidi.MidiIn() 84 | midi_in.callback = callback 85 | midi_in.open_port(0) 86 | 87 | # do something else here (but don't quit) 88 | 89 | Note that the signature of the callback differs from the original `RtMidi`_ 90 | API: ``message`` is now the first parameter, like in the tuple returned by 91 | ``get_message()``. 92 | 93 | License 94 | ------- 95 | 96 | *rtmidi-python* is licensed under the MIT License, see LICENSE. 97 | 98 | It uses `RtMidi`_, licensed under a modified MIT License, see 99 | RtMidi/RtMidi.h. 100 | 101 | .. _RtMidi: http://www.music.mcgill.ca/~gary/rtmidi/ 102 | .. _PyPI: https://pypi.python.org/pypi/rtmidi-python#downloads 103 | .. _Cython: http://www.cython.org 104 | -------------------------------------------------------------------------------- /RtMidi/RtError.h: -------------------------------------------------------------------------------- 1 | /************************************************************************/ 2 | /*! \class RtError 3 | \brief Exception handling class for RtAudio & RtMidi. 4 | 5 | The RtError class is quite simple but it does allow errors to be 6 | "caught" by RtError::Type. See the RtAudio and RtMidi 7 | documentation to know which methods can throw an RtError. 8 | 9 | */ 10 | /************************************************************************/ 11 | 12 | #ifndef RTERROR_H 13 | #define RTERROR_H 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | class RtError : public std::exception 20 | { 21 | public: 22 | //! Defined RtError types. 23 | enum Type { 24 | WARNING, /*!< A non-critical error. */ 25 | DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ 26 | UNSPECIFIED, /*!< The default, unspecified error type. */ 27 | NO_DEVICES_FOUND, /*!< No devices found on system. */ 28 | INVALID_DEVICE, /*!< An invalid device ID was specified. */ 29 | MEMORY_ERROR, /*!< An error occured during memory allocation. */ 30 | INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ 31 | INVALID_USE, /*!< The function was called incorrectly. */ 32 | DRIVER_ERROR, /*!< A system driver error occured. */ 33 | SYSTEM_ERROR, /*!< A system error occured. */ 34 | THREAD_ERROR /*!< A thread error occured. */ 35 | }; 36 | 37 | //! The constructor. 38 | RtError( const std::string& message, Type type = RtError::UNSPECIFIED ) throw() : message_(message), type_(type) {} 39 | 40 | //! The destructor. 41 | virtual ~RtError( void ) throw() {} 42 | 43 | //! Prints thrown error message to stderr. 44 | virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } 45 | 46 | //! Returns the thrown error message type. 47 | virtual const Type& getType(void) const throw() { return type_; } 48 | 49 | //! Returns the thrown error message string. 50 | virtual const std::string& getMessage(void) const throw() { return message_; } 51 | 52 | //! Returns the thrown error message as a c-style string. 53 | virtual const char* what( void ) const throw() { return message_.c_str(); } 54 | 55 | protected: 56 | std::string message_; 57 | Type type_; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /RtMidi/RtMidi.cpp: -------------------------------------------------------------------------------- 1 | /**********************************************************************/ 2 | /*! \class RtMidi 3 | \brief An abstract base class for realtime MIDI input/output. 4 | 5 | This class implements some common functionality for the realtime 6 | MIDI input/output subclasses RtMidiIn and RtMidiOut. 7 | 8 | RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ 9 | 10 | RtMidi: realtime MIDI i/o C++ classes 11 | Copyright (c) 2003-2012 Gary P. Scavone 12 | 13 | Permission is hereby granted, free of charge, to any person 14 | obtaining a copy of this software and associated documentation files 15 | (the "Software"), to deal in the Software without restriction, 16 | including without limitation the rights to use, copy, modify, merge, 17 | publish, distribute, sublicense, and/or sell copies of the Software, 18 | and to permit persons to whom the Software is furnished to do so, 19 | subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be 22 | included in all copies or substantial portions of the Software. 23 | 24 | Any person wishing to distribute modifications to the Software is 25 | asked to send the modifications to the original developer so that 26 | they can be incorporated into the canonical version. This is, 27 | however, not a binding provision of this license. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 32 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 33 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 34 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | */ 37 | /**********************************************************************/ 38 | 39 | // RtMidi: Version 2.0.1 40 | 41 | #include "RtMidi.h" 42 | #include 43 | 44 | //*********************************************************************// 45 | // RtMidi Definitions 46 | //*********************************************************************// 47 | 48 | void RtMidi :: getCompiledApi( std::vector &apis ) throw() 49 | { 50 | apis.clear(); 51 | 52 | // The order here will control the order of RtMidi's API search in 53 | // the constructor. 54 | #if defined(__MACOSX_CORE__) 55 | apis.push_back( MACOSX_CORE ); 56 | #endif 57 | #if defined(__LINUX_ALSA__) 58 | apis.push_back( LINUX_ALSA ); 59 | #endif 60 | #if defined(__UNIX_JACK__) 61 | apis.push_back( UNIX_JACK ); 62 | #endif 63 | #if defined(__WINDOWS_MM__) 64 | apis.push_back( WINDOWS_MM ); 65 | #endif 66 | #if defined(__WINDOWS_KS__) 67 | apis.push_back( WINDOWS_KS ); 68 | #endif 69 | #if defined(__RTMIDI_DUMMY__) 70 | apis.push_back( RTMIDI_DUMMY ); 71 | #endif 72 | } 73 | 74 | void RtMidi :: error( RtError::Type type, std::string errorString ) 75 | { 76 | if (type == RtError::WARNING) { 77 | std::cerr << '\n' << errorString << "\n\n"; 78 | } 79 | else if (type == RtError::DEBUG_WARNING) { 80 | #if defined(__RTMIDI_DEBUG__) 81 | std::cerr << '\n' << errorString << "\n\n"; 82 | #endif 83 | } 84 | else { 85 | std::cerr << '\n' << errorString << "\n\n"; 86 | throw RtError( errorString, type ); 87 | } 88 | } 89 | 90 | //*********************************************************************// 91 | // RtMidiIn Definitions 92 | //*********************************************************************// 93 | 94 | void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) 95 | { 96 | if ( rtapi_ ) 97 | delete rtapi_; 98 | rtapi_ = 0; 99 | 100 | #if defined(__UNIX_JACK__) 101 | if ( api == UNIX_JACK ) 102 | rtapi_ = new MidiInJack( clientName, queueSizeLimit ); 103 | #endif 104 | #if defined(__LINUX_ALSA__) 105 | if ( api == LINUX_ALSA ) 106 | rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); 107 | #endif 108 | #if defined(__WINDOWS_MM__) 109 | if ( api == WINDOWS_MM ) 110 | rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); 111 | #endif 112 | #if defined(__WINDOWS_KS__) 113 | if ( api == WINDOWS_KS ) 114 | rtapi_ = new MidiInWinKS( clientName, queueSizeLimit ); 115 | #endif 116 | #if defined(__MACOSX_CORE__) 117 | if ( api == MACOSX_CORE ) 118 | rtapi_ = new MidiInCore( clientName, queueSizeLimit ); 119 | #endif 120 | #if defined(__RTMIDI_DUMMY__) 121 | if ( api == RTMIDI_DUMMY ) 122 | rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); 123 | #endif 124 | } 125 | 126 | RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) 127 | { 128 | rtapi_ = 0; 129 | 130 | if ( api != UNSPECIFIED ) { 131 | // Attempt to open the specified API. 132 | openMidiApi( api, clientName, queueSizeLimit ); 133 | if ( rtapi_ ) return; 134 | 135 | // No compiled support for specified API value. Issue a debug 136 | // warning and continue as if no API was specified. 137 | RtMidi::error( RtError::WARNING, "RtMidiIn: no compiled support for specified API argument!" ); 138 | } 139 | 140 | // Iterate through the compiled APIs and return as soon as we find 141 | // one with at least one port or we reach the end of the list. 142 | std::vector< RtMidi::Api > apis; 143 | getCompiledApi( apis ); 144 | for ( unsigned int i=0; igetPortCount() ) break; 147 | } 148 | 149 | if ( rtapi_ ) return; 150 | 151 | // It should not be possible to get here because the preprocessor 152 | // definition __RTMIDI_DUMMY__ is automatically defined if no 153 | // API-specific definitions are passed to the compiler. But just in 154 | // case something weird happens, we'll print out an error message. 155 | RtMidi::error( RtError::WARNING, "RtMidiIn: no compiled API support found ... critical error!!" ); 156 | } 157 | 158 | RtMidiIn :: ~RtMidiIn() throw() 159 | { 160 | delete rtapi_; 161 | } 162 | 163 | 164 | //*********************************************************************// 165 | // RtMidiOut Definitions 166 | //*********************************************************************// 167 | 168 | void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName ) 169 | { 170 | if ( rtapi_ ) 171 | delete rtapi_; 172 | rtapi_ = 0; 173 | 174 | #if defined(__UNIX_JACK__) 175 | if ( api == UNIX_JACK ) 176 | rtapi_ = new MidiOutJack( clientName ); 177 | #endif 178 | #if defined(__LINUX_ALSA__) 179 | if ( api == LINUX_ALSA ) 180 | rtapi_ = new MidiOutAlsa( clientName ); 181 | #endif 182 | #if defined(__WINDOWS_MM__) 183 | if ( api == WINDOWS_MM ) 184 | rtapi_ = new MidiOutWinMM( clientName ); 185 | #endif 186 | #if defined(__WINDOWS_KS__) 187 | if ( api == WINDOWS_KS ) 188 | rtapi_ = new MidiOutWinKS( clientName ); 189 | #endif 190 | #if defined(__MACOSX_CORE__) 191 | if ( api == MACOSX_CORE ) 192 | rtapi_ = new MidiOutCore( clientName ); 193 | #endif 194 | #if defined(__RTMIDI_DUMMY__) 195 | if ( api == RTMIDI_DUMMY ) 196 | rtapi_ = new MidiOutDummy( clientName ); 197 | #endif 198 | } 199 | 200 | RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName ) 201 | { 202 | rtapi_ = 0; 203 | 204 | if ( api != UNSPECIFIED ) { 205 | // Attempt to open the specified API. 206 | openMidiApi( api, clientName ); 207 | if ( rtapi_ ) return; 208 | 209 | // No compiled support for specified API value. Issue a debug 210 | // warning and continue as if no API was specified. 211 | RtMidi::error( RtError::WARNING, "RtMidiOut: no compiled support for specified API argument!" ); 212 | } 213 | 214 | // Iterate through the compiled APIs and return as soon as we find 215 | // one with at least one port or we reach the end of the list. 216 | std::vector< RtMidi::Api > apis; 217 | getCompiledApi( apis ); 218 | for ( unsigned int i=0; igetPortCount() ) break; 221 | } 222 | 223 | if ( rtapi_ ) return; 224 | 225 | // It should not be possible to get here because the preprocessor 226 | // definition __RTMIDI_DUMMY__ is automatically defined if no 227 | // API-specific definitions are passed to the compiler. But just in 228 | // case something weird happens, we'll print out an error message. 229 | RtMidi::error( RtError::WARNING, "RtMidiOut: no compiled API support found ... critical error!!" ); 230 | } 231 | 232 | RtMidiOut :: ~RtMidiOut() throw() 233 | { 234 | delete rtapi_; 235 | } 236 | 237 | //*********************************************************************// 238 | // Common MidiInApi Definitions 239 | //*********************************************************************// 240 | 241 | MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) 242 | : apiData_( 0 ), connected_( false ) 243 | { 244 | // Allocate the MIDI queue. 245 | inputData_.queue.ringSize = queueSizeLimit; 246 | if ( inputData_.queue.ringSize > 0 ) 247 | inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; 248 | } 249 | 250 | MidiInApi :: ~MidiInApi( void ) 251 | { 252 | // Delete the MIDI queue. 253 | if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; 254 | } 255 | 256 | void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) 257 | { 258 | if ( inputData_.usingCallback ) { 259 | errorString_ = "MidiInApi::setCallback: a callback function is already set!"; 260 | RtMidi::error( RtError::WARNING, errorString_ ); 261 | return; 262 | } 263 | 264 | if ( !callback ) { 265 | errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; 266 | RtMidi::error( RtError::WARNING, errorString_ ); 267 | return; 268 | } 269 | 270 | inputData_.userCallback = (void *) callback; 271 | inputData_.userData = userData; 272 | inputData_.usingCallback = true; 273 | } 274 | 275 | void MidiInApi :: cancelCallback() 276 | { 277 | if ( !inputData_.usingCallback ) { 278 | errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; 279 | RtMidi::error( RtError::WARNING, errorString_ ); 280 | return; 281 | } 282 | 283 | inputData_.userCallback = 0; 284 | inputData_.userData = 0; 285 | inputData_.usingCallback = false; 286 | } 287 | 288 | void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) 289 | { 290 | inputData_.ignoreFlags = 0; 291 | if ( midiSysex ) inputData_.ignoreFlags = 0x01; 292 | if ( midiTime ) inputData_.ignoreFlags |= 0x02; 293 | if ( midiSense ) inputData_.ignoreFlags |= 0x04; 294 | } 295 | 296 | double MidiInApi :: getMessage( std::vector *message ) 297 | { 298 | message->clear(); 299 | 300 | if ( inputData_.usingCallback ) { 301 | errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; 302 | RtMidi::error( RtError::WARNING, errorString_ ); 303 | return 0.0; 304 | } 305 | 306 | if ( inputData_.queue.size == 0 ) return 0.0; 307 | 308 | // Copy queued message to the vector pointer argument and then "pop" it. 309 | std::vector *bytes = &(inputData_.queue.ring[inputData_.queue.front].bytes); 310 | message->assign( bytes->begin(), bytes->end() ); 311 | double deltaTime = inputData_.queue.ring[inputData_.queue.front].timeStamp; 312 | inputData_.queue.size--; 313 | inputData_.queue.front++; 314 | if ( inputData_.queue.front == inputData_.queue.ringSize ) 315 | inputData_.queue.front = 0; 316 | 317 | return deltaTime; 318 | } 319 | 320 | //*********************************************************************// 321 | // Common MidiOutApi Definitions 322 | //*********************************************************************// 323 | 324 | MidiOutApi :: MidiOutApi( void ) 325 | : apiData_( 0 ), connected_( false ) 326 | { 327 | } 328 | 329 | MidiOutApi :: ~MidiOutApi( void ) 330 | { 331 | } 332 | 333 | // *************************************************** // 334 | // 335 | // OS/API-specific methods. 336 | // 337 | // *************************************************** // 338 | 339 | #if defined(__MACOSX_CORE__) 340 | 341 | // The CoreMIDI API is based on the use of a callback function for 342 | // MIDI input. We convert the system specific time stamps to delta 343 | // time values. 344 | 345 | // OS-X CoreMIDI header files. 346 | #include 347 | #include 348 | #include 349 | 350 | // A structure to hold variables related to the CoreMIDI API 351 | // implementation. 352 | struct CoreMidiData { 353 | MIDIClientRef client; 354 | MIDIPortRef port; 355 | MIDIEndpointRef endpoint; 356 | MIDIEndpointRef destinationId; 357 | unsigned long long lastTime; 358 | MIDISysexSendRequest sysexreq; 359 | }; 360 | 361 | //*********************************************************************// 362 | // API: OS-X 363 | // Class Definitions: MidiInCore 364 | //*********************************************************************// 365 | 366 | void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) 367 | { 368 | MidiInApi::RtMidiInData *data = static_cast (procRef); 369 | CoreMidiData *apiData = static_cast (data->apiData); 370 | 371 | unsigned char status; 372 | unsigned short nBytes, iByte, size; 373 | unsigned long long time; 374 | 375 | bool& continueSysex = data->continueSysex; 376 | MidiInApi::MidiMessage& message = data->message; 377 | 378 | const MIDIPacket *packet = &list->packet[0]; 379 | for ( unsigned int i=0; inumPackets; ++i ) { 380 | 381 | // My interpretation of the CoreMIDI documentation: all message 382 | // types, except sysex, are complete within a packet and there may 383 | // be several of them in a single packet. Sysex messages can be 384 | // broken across multiple packets and PacketLists but are bundled 385 | // alone within each packet (these packets do not contain other 386 | // message types). If sysex messages are split across multiple 387 | // MIDIPacketLists, they must be handled by multiple calls to this 388 | // function. 389 | 390 | nBytes = packet->length; 391 | if ( nBytes == 0 ) continue; 392 | 393 | // Calculate time stamp. 394 | 395 | if ( data->firstMessage ) { 396 | message.timeStamp = 0.0; 397 | data->firstMessage = false; 398 | } 399 | else { 400 | time = packet->timeStamp; 401 | if ( time == 0 ) { // this happens when receiving asynchronous sysex messages 402 | time = AudioGetCurrentHostTime(); 403 | } 404 | time -= apiData->lastTime; 405 | time = AudioConvertHostTimeToNanos( time ); 406 | if ( !continueSysex ) 407 | message.timeStamp = time * 0.000000001; 408 | } 409 | apiData->lastTime = packet->timeStamp; 410 | if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages 411 | apiData->lastTime = AudioGetCurrentHostTime(); 412 | } 413 | //std::cout << "TimeStamp = " << packet->timeStamp << std::endl; 414 | 415 | iByte = 0; 416 | if ( continueSysex ) { 417 | // We have a continuing, segmented sysex message. 418 | if ( !( data->ignoreFlags & 0x01 ) ) { 419 | // If we're not ignoring sysex messages, copy the entire packet. 420 | for ( unsigned int j=0; jdata[j] ); 422 | } 423 | continueSysex = packet->data[nBytes-1] != 0xF7; 424 | 425 | if ( !continueSysex ) { 426 | // If not a continuing sysex message, invoke the user callback function or queue the message. 427 | if ( data->usingCallback ) { 428 | RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 429 | callback( message.timeStamp, &message.bytes, data->userData ); 430 | } 431 | else { 432 | // As long as we haven't reached our queue size limit, push the message. 433 | if ( data->queue.size < data->queue.ringSize ) { 434 | data->queue.ring[data->queue.back++] = message; 435 | if ( data->queue.back == data->queue.ringSize ) 436 | data->queue.back = 0; 437 | data->queue.size++; 438 | } 439 | else 440 | std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; 441 | } 442 | message.bytes.clear(); 443 | } 444 | } 445 | else { 446 | while ( iByte < nBytes ) { 447 | size = 0; 448 | // We are expecting that the next byte in the packet is a status byte. 449 | status = packet->data[iByte]; 450 | if ( !(status & 0x80) ) break; 451 | // Determine the number of bytes in the MIDI message. 452 | if ( status < 0xC0 ) size = 3; 453 | else if ( status < 0xE0 ) size = 2; 454 | else if ( status < 0xF0 ) size = 3; 455 | else if ( status == 0xF0 ) { 456 | // A MIDI sysex 457 | if ( data->ignoreFlags & 0x01 ) { 458 | size = 0; 459 | iByte = nBytes; 460 | } 461 | else size = nBytes - iByte; 462 | continueSysex = packet->data[nBytes-1] != 0xF7; 463 | } 464 | else if ( status == 0xF1 ) { 465 | // A MIDI time code message 466 | if ( data->ignoreFlags & 0x02 ) { 467 | size = 0; 468 | iByte += 2; 469 | } 470 | else size = 2; 471 | } 472 | else if ( status == 0xF2 ) size = 3; 473 | else if ( status == 0xF3 ) size = 2; 474 | else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { 475 | // A MIDI timing tick message and we're ignoring it. 476 | size = 0; 477 | iByte += 1; 478 | } 479 | else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { 480 | // A MIDI active sensing message and we're ignoring it. 481 | size = 0; 482 | iByte += 1; 483 | } 484 | else size = 1; 485 | 486 | // Copy the MIDI data to our vector. 487 | if ( size ) { 488 | message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); 489 | if ( !continueSysex ) { 490 | // If not a continuing sysex message, invoke the user callback function or queue the message. 491 | if ( data->usingCallback ) { 492 | RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 493 | callback( message.timeStamp, &message.bytes, data->userData ); 494 | } 495 | else { 496 | // As long as we haven't reached our queue size limit, push the message. 497 | if ( data->queue.size < data->queue.ringSize ) { 498 | data->queue.ring[data->queue.back++] = message; 499 | if ( data->queue.back == data->queue.ringSize ) 500 | data->queue.back = 0; 501 | data->queue.size++; 502 | } 503 | else 504 | std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; 505 | } 506 | message.bytes.clear(); 507 | } 508 | iByte += size; 509 | } 510 | } 511 | } 512 | packet = MIDIPacketNext(packet); 513 | } 514 | } 515 | 516 | MidiInCore :: MidiInCore( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) 517 | { 518 | initialize( clientName ); 519 | } 520 | 521 | MidiInCore :: ~MidiInCore( void ) 522 | { 523 | // Close a connection if it exists. 524 | closePort(); 525 | 526 | // Cleanup. 527 | CoreMidiData *data = static_cast (apiData_); 528 | MIDIClientDispose( data->client ); 529 | if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); 530 | delete data; 531 | } 532 | 533 | void MidiInCore :: initialize( const std::string& clientName ) 534 | { 535 | // Set up our client. 536 | MIDIClientRef client; 537 | OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); 538 | if ( result != noErr ) { 539 | errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object."; 540 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 541 | } 542 | 543 | // Save our api-specific connection information. 544 | CoreMidiData *data = (CoreMidiData *) new CoreMidiData; 545 | data->client = client; 546 | data->endpoint = 0; 547 | apiData_ = (void *) data; 548 | inputData_.apiData = (void *) data; 549 | } 550 | 551 | void MidiInCore :: openPort( unsigned int portNumber, const std::string portName ) 552 | { 553 | if ( connected_ ) { 554 | errorString_ = "MidiInCore::openPort: a valid connection already exists!"; 555 | RtMidi::error( RtError::WARNING, errorString_ ); 556 | return; 557 | } 558 | 559 | unsigned int nSrc = MIDIGetNumberOfSources(); 560 | if (nSrc < 1) { 561 | errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; 562 | RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 563 | } 564 | 565 | std::ostringstream ost; 566 | if ( portNumber >= nSrc ) { 567 | ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 568 | errorString_ = ost.str(); 569 | RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 570 | } 571 | 572 | MIDIPortRef port; 573 | CoreMidiData *data = static_cast (apiData_); 574 | OSStatus result = MIDIInputPortCreate( data->client, 575 | CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 576 | midiInputCallback, (void *)&inputData_, &port ); 577 | if ( result != noErr ) { 578 | MIDIClientDispose( data->client ); 579 | errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; 580 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 581 | } 582 | 583 | // Get the desired input source identifier. 584 | MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); 585 | if ( endpoint == 0 ) { 586 | MIDIPortDispose( port ); 587 | MIDIClientDispose( data->client ); 588 | errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; 589 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 590 | } 591 | 592 | // Make the connection. 593 | result = MIDIPortConnectSource( port, endpoint, NULL ); 594 | if ( result != noErr ) { 595 | MIDIPortDispose( port ); 596 | MIDIClientDispose( data->client ); 597 | errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; 598 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 599 | } 600 | 601 | // Save our api-specific port information. 602 | data->port = port; 603 | 604 | connected_ = true; 605 | } 606 | 607 | void MidiInCore :: openVirtualPort( const std::string portName ) 608 | { 609 | CoreMidiData *data = static_cast (apiData_); 610 | 611 | // Create a virtual MIDI input destination. 612 | MIDIEndpointRef endpoint; 613 | OSStatus result = MIDIDestinationCreate( data->client, 614 | CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 615 | midiInputCallback, (void *)&inputData_, &endpoint ); 616 | if ( result != noErr ) { 617 | errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; 618 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 619 | } 620 | 621 | // Save our api-specific connection information. 622 | data->endpoint = endpoint; 623 | } 624 | 625 | void MidiInCore :: closePort( void ) 626 | { 627 | if ( connected_ ) { 628 | CoreMidiData *data = static_cast (apiData_); 629 | MIDIPortDispose( data->port ); 630 | connected_ = false; 631 | } 632 | } 633 | 634 | unsigned int MidiInCore :: getPortCount() 635 | { 636 | return MIDIGetNumberOfSources(); 637 | } 638 | 639 | // This function was submitted by Douglas Casey Tucker and apparently 640 | // derived largely from PortMidi. 641 | CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) 642 | { 643 | CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); 644 | CFStringRef str; 645 | 646 | // Begin with the endpoint's name. 647 | str = NULL; 648 | MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); 649 | if ( str != NULL ) { 650 | CFStringAppend( result, str ); 651 | CFRelease( str ); 652 | } 653 | 654 | MIDIEntityRef entity = NULL; 655 | MIDIEndpointGetEntity( endpoint, &entity ); 656 | if ( entity == 0 ) 657 | // probably virtual 658 | return result; 659 | 660 | if ( CFStringGetLength( result ) == 0 ) { 661 | // endpoint name has zero length -- try the entity 662 | str = NULL; 663 | MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); 664 | if ( str != NULL ) { 665 | CFStringAppend( result, str ); 666 | CFRelease( str ); 667 | } 668 | } 669 | // now consider the device's name 670 | MIDIDeviceRef device = 0; 671 | MIDIEntityGetDevice( entity, &device ); 672 | if ( device == 0 ) 673 | return result; 674 | 675 | str = NULL; 676 | MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); 677 | if ( CFStringGetLength( result ) == 0 ) { 678 | CFRelease( result ); 679 | return str; 680 | } 681 | if ( str != NULL ) { 682 | // if an external device has only one entity, throw away 683 | // the endpoint name and just use the device name 684 | if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { 685 | CFRelease( result ); 686 | return str; 687 | } else { 688 | if ( CFStringGetLength( str ) == 0 ) { 689 | CFRelease( str ); 690 | return result; 691 | } 692 | // does the entity name already start with the device name? 693 | // (some drivers do this though they shouldn't) 694 | // if so, do not prepend 695 | if ( CFStringCompareWithOptions( result, /* endpoint name */ 696 | str /* device name */, 697 | CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { 698 | // prepend the device name to the entity name 699 | if ( CFStringGetLength( result ) > 0 ) 700 | CFStringInsert( result, 0, CFSTR(" ") ); 701 | CFStringInsert( result, 0, str ); 702 | } 703 | CFRelease( str ); 704 | } 705 | } 706 | return result; 707 | } 708 | 709 | // This function was submitted by Douglas Casey Tucker and apparently 710 | // derived largely from PortMidi. 711 | static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) 712 | { 713 | CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); 714 | CFStringRef str; 715 | OSStatus err; 716 | int i; 717 | 718 | // Does the endpoint have connections? 719 | CFDataRef connections = NULL; 720 | int nConnected = 0; 721 | bool anyStrings = false; 722 | err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); 723 | if ( connections != NULL ) { 724 | // It has connections, follow them 725 | // Concatenate the names of all connected devices 726 | nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); 727 | if ( nConnected ) { 728 | const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); 729 | for ( i=0; i= MIDIGetNumberOfSources() ) { 772 | ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 773 | errorString_ = ost.str(); 774 | RtMidi::error( RtError::WARNING, errorString_ ); 775 | //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 776 | return stringName; 777 | } 778 | 779 | portRef = MIDIGetSource( portNumber ); 780 | nameRef = ConnectedEndpointName(portRef); 781 | CFStringGetCString( nameRef, name, sizeof(name), 0); 782 | CFRelease( nameRef ); 783 | 784 | return stringName = name; 785 | } 786 | 787 | //*********************************************************************// 788 | // API: OS-X 789 | // Class Definitions: MidiOutCore 790 | //*********************************************************************// 791 | 792 | MidiOutCore :: MidiOutCore( const std::string clientName ) : MidiOutApi() 793 | { 794 | initialize( clientName ); 795 | } 796 | 797 | MidiOutCore :: ~MidiOutCore( void ) 798 | { 799 | // Close a connection if it exists. 800 | closePort(); 801 | 802 | // Cleanup. 803 | CoreMidiData *data = static_cast (apiData_); 804 | MIDIClientDispose( data->client ); 805 | if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); 806 | delete data; 807 | } 808 | 809 | void MidiOutCore :: initialize( const std::string& clientName ) 810 | { 811 | // Set up our client. 812 | MIDIClientRef client; 813 | OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); 814 | if ( result != noErr ) { 815 | errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object."; 816 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 817 | } 818 | 819 | // Save our api-specific connection information. 820 | CoreMidiData *data = (CoreMidiData *) new CoreMidiData; 821 | data->client = client; 822 | data->endpoint = 0; 823 | apiData_ = (void *) data; 824 | } 825 | 826 | unsigned int MidiOutCore :: getPortCount() 827 | { 828 | return MIDIGetNumberOfDestinations(); 829 | } 830 | 831 | std::string MidiOutCore :: getPortName( unsigned int portNumber ) 832 | { 833 | CFStringRef nameRef; 834 | MIDIEndpointRef portRef; 835 | std::ostringstream ost; 836 | char name[128]; 837 | 838 | std::string stringName; 839 | if ( portNumber >= MIDIGetNumberOfDestinations() ) { 840 | ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 841 | errorString_ = ost.str(); 842 | RtMidi::error( RtError::WARNING, errorString_ ); 843 | return stringName; 844 | //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 845 | } 846 | 847 | portRef = MIDIGetDestination( portNumber ); 848 | nameRef = ConnectedEndpointName(portRef); 849 | CFStringGetCString( nameRef, name, sizeof(name), 0); 850 | CFRelease( nameRef ); 851 | 852 | return stringName = name; 853 | } 854 | 855 | void MidiOutCore :: openPort( unsigned int portNumber, const std::string portName ) 856 | { 857 | if ( connected_ ) { 858 | errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; 859 | RtMidi::error( RtError::WARNING, errorString_ ); 860 | return; 861 | } 862 | 863 | unsigned int nDest = MIDIGetNumberOfDestinations(); 864 | if (nDest < 1) { 865 | errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; 866 | RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 867 | } 868 | 869 | std::ostringstream ost; 870 | if ( portNumber >= nDest ) { 871 | ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 872 | errorString_ = ost.str(); 873 | RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 874 | } 875 | 876 | MIDIPortRef port; 877 | CoreMidiData *data = static_cast (apiData_); 878 | OSStatus result = MIDIOutputPortCreate( data->client, 879 | CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 880 | &port ); 881 | if ( result != noErr ) { 882 | MIDIClientDispose( data->client ); 883 | errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; 884 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 885 | } 886 | 887 | // Get the desired output port identifier. 888 | MIDIEndpointRef destination = MIDIGetDestination( portNumber ); 889 | if ( destination == 0 ) { 890 | MIDIPortDispose( port ); 891 | MIDIClientDispose( data->client ); 892 | errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; 893 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 894 | } 895 | 896 | // Save our api-specific connection information. 897 | data->port = port; 898 | data->destinationId = destination; 899 | connected_ = true; 900 | } 901 | 902 | void MidiOutCore :: closePort( void ) 903 | { 904 | if ( connected_ ) { 905 | CoreMidiData *data = static_cast (apiData_); 906 | MIDIPortDispose( data->port ); 907 | connected_ = false; 908 | } 909 | } 910 | 911 | void MidiOutCore :: openVirtualPort( std::string portName ) 912 | { 913 | CoreMidiData *data = static_cast (apiData_); 914 | 915 | if ( data->endpoint ) { 916 | errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; 917 | RtMidi::error( RtError::WARNING, errorString_ ); 918 | return; 919 | } 920 | 921 | // Create a virtual MIDI output source. 922 | MIDIEndpointRef endpoint; 923 | OSStatus result = MIDISourceCreate( data->client, 924 | CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), 925 | &endpoint ); 926 | if ( result != noErr ) { 927 | errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; 928 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 929 | } 930 | 931 | // Save our api-specific connection information. 932 | data->endpoint = endpoint; 933 | } 934 | 935 | char *sysexBuffer = 0; 936 | 937 | void sysexCompletionProc( MIDISysexSendRequest * sreq ) 938 | { 939 | //std::cout << "Completed SysEx send\n"; 940 | delete sysexBuffer; 941 | sysexBuffer = 0; 942 | } 943 | 944 | void MidiOutCore :: sendMessage( std::vector *message ) 945 | { 946 | // We use the MIDISendSysex() function to asynchronously send sysex 947 | // messages. Otherwise, we use a single CoreMidi MIDIPacket. 948 | unsigned int nBytes = message->size(); 949 | if ( nBytes == 0 ) { 950 | errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; 951 | RtMidi::error( RtError::WARNING, errorString_ ); 952 | return; 953 | } 954 | 955 | // unsigned int packetBytes, bytesLeft = nBytes; 956 | // unsigned int messageIndex = 0; 957 | MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); 958 | CoreMidiData *data = static_cast (apiData_); 959 | OSStatus result; 960 | 961 | if ( message->at(0) == 0xF0 ) { 962 | 963 | while ( sysexBuffer != 0 ) usleep( 1000 ); // sleep 1 ms 964 | 965 | sysexBuffer = new char[nBytes]; 966 | if ( sysexBuffer == NULL ) { 967 | errorString_ = "MidiOutCore::sendMessage: error allocating sysex message memory!"; 968 | RtMidi::error( RtError::MEMORY_ERROR, errorString_ ); 969 | } 970 | 971 | // Copy data to buffer. 972 | for ( unsigned int i=0; iat(i); 973 | 974 | data->sysexreq.destination = data->destinationId; 975 | data->sysexreq.data = (Byte *)sysexBuffer; 976 | data->sysexreq.bytesToSend = nBytes; 977 | data->sysexreq.complete = 0; 978 | data->sysexreq.completionProc = sysexCompletionProc; 979 | data->sysexreq.completionRefCon = &(data->sysexreq); 980 | 981 | result = MIDISendSysex( &(data->sysexreq) ); 982 | if ( result != noErr ) { 983 | errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; 984 | RtMidi::error( RtError::WARNING, errorString_ ); 985 | } 986 | return; 987 | } 988 | else if ( nBytes > 3 ) { 989 | errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; 990 | RtMidi::error( RtError::WARNING, errorString_ ); 991 | return; 992 | } 993 | 994 | MIDIPacketList packetList; 995 | MIDIPacket *packet = MIDIPacketListInit( &packetList ); 996 | packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) ); 997 | if ( !packet ) { 998 | errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; 999 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1000 | } 1001 | 1002 | // Send to any destinations that may have connected to us. 1003 | if ( data->endpoint ) { 1004 | result = MIDIReceived( data->endpoint, &packetList ); 1005 | if ( result != noErr ) { 1006 | errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; 1007 | RtMidi::error( RtError::WARNING, errorString_ ); 1008 | } 1009 | } 1010 | 1011 | // And send to an explicit destination port if we're connected. 1012 | if ( connected_ ) { 1013 | result = MIDISend( data->port, data->destinationId, &packetList ); 1014 | if ( result != noErr ) { 1015 | errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; 1016 | RtMidi::error( RtError::WARNING, errorString_ ); 1017 | } 1018 | } 1019 | 1020 | } 1021 | 1022 | #endif // __MACOSX_CORE__ 1023 | 1024 | 1025 | //*********************************************************************// 1026 | // API: LINUX ALSA SEQUENCER 1027 | //*********************************************************************// 1028 | 1029 | // API information found at: 1030 | // - http://www.alsa-project.org/documentation.php#Library 1031 | 1032 | #if defined(__LINUX_ALSA__) 1033 | 1034 | // The ALSA Sequencer API is based on the use of a callback function for 1035 | // MIDI input. 1036 | // 1037 | // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer 1038 | // time stamps and other assorted fixes!!! 1039 | 1040 | // If you don't need timestamping for incoming MIDI events, define the 1041 | // preprocessor definition AVOID_TIMESTAMPING to save resources 1042 | // associated with the ALSA sequencer queues. 1043 | 1044 | #include 1045 | #include 1046 | 1047 | // ALSA header file. 1048 | #include 1049 | 1050 | // Global sequencer instance created when first In/Out object is 1051 | // created, then destroyed when last In/Out is deleted. 1052 | static snd_seq_t *s_seq = NULL; 1053 | 1054 | // Variable to keep track of how many ports are open. 1055 | static unsigned int s_numPorts = 0; 1056 | 1057 | // The client name to use when creating the sequencer, which is 1058 | // currently set on the first call to createSequencer. 1059 | static std::string s_clientName = "RtMidi Client"; 1060 | 1061 | // A structure to hold variables related to the ALSA API 1062 | // implementation. 1063 | struct AlsaMidiData { 1064 | snd_seq_t *seq; 1065 | unsigned int portNum; 1066 | int vport; 1067 | snd_seq_port_subscribe_t *subscription; 1068 | snd_midi_event_t *coder; 1069 | unsigned int bufferSize; 1070 | unsigned char *buffer; 1071 | pthread_t thread; 1072 | pthread_t dummy_thread_id; 1073 | unsigned long long lastTime; 1074 | int queue_id; // an input queue is needed to get timestamped events 1075 | int trigger_fds[2]; 1076 | }; 1077 | 1078 | #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) 1079 | 1080 | snd_seq_t* createSequencer( const std::string& clientName ) 1081 | { 1082 | // Set up the ALSA sequencer client. 1083 | if ( s_seq == NULL ) { 1084 | int result = snd_seq_open(&s_seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); 1085 | if ( result < 0 ) { 1086 | s_seq = NULL; 1087 | } 1088 | else { 1089 | // Set client name, use current name if given string is empty. 1090 | if ( clientName != "" ) { 1091 | s_clientName = clientName; 1092 | } 1093 | snd_seq_set_client_name( s_seq, s_clientName.c_str() ); 1094 | } 1095 | } 1096 | 1097 | // Increment port count. 1098 | s_numPorts++; 1099 | 1100 | return s_seq; 1101 | } 1102 | 1103 | void freeSequencer ( void ) 1104 | { 1105 | s_numPorts--; 1106 | if ( s_numPorts == 0 && s_seq != NULL ) { 1107 | snd_seq_close( s_seq ); 1108 | s_seq = NULL; 1109 | } 1110 | } 1111 | 1112 | //*********************************************************************// 1113 | // API: LINUX ALSA 1114 | // Class Definitions: MidiInAlsa 1115 | //*********************************************************************// 1116 | 1117 | extern "C" void *alsaMidiHandler( void *ptr ) 1118 | { 1119 | MidiInApi::RtMidiInData *data = static_cast (ptr); 1120 | AlsaMidiData *apiData = static_cast (data->apiData); 1121 | 1122 | long nBytes; 1123 | unsigned long long time, lastTime; 1124 | bool continueSysex = false; 1125 | bool doDecode = false; 1126 | MidiInApi::MidiMessage message; 1127 | int poll_fd_count; 1128 | struct pollfd *poll_fds; 1129 | 1130 | snd_seq_event_t *ev; 1131 | int result; 1132 | apiData->bufferSize = 32; 1133 | result = snd_midi_event_new( 0, &apiData->coder ); 1134 | if ( result < 0 ) { 1135 | data->doInput = false; 1136 | std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; 1137 | return 0; 1138 | } 1139 | unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); 1140 | if ( buffer == NULL ) { 1141 | data->doInput = false; 1142 | snd_midi_event_free( apiData->coder ); 1143 | apiData->coder = 0; 1144 | std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; 1145 | return 0; 1146 | } 1147 | snd_midi_event_init( apiData->coder ); 1148 | snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages 1149 | 1150 | poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; 1151 | poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); 1152 | snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); 1153 | poll_fds[0].fd = apiData->trigger_fds[0]; 1154 | poll_fds[0].events = POLLIN; 1155 | 1156 | while ( data->doInput ) { 1157 | 1158 | if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { 1159 | // No data pending 1160 | if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { 1161 | if ( poll_fds[0].revents & POLLIN ) { 1162 | bool dummy; 1163 | int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); 1164 | (void) res; 1165 | } 1166 | } 1167 | continue; 1168 | } 1169 | 1170 | // If here, there should be data. 1171 | result = snd_seq_event_input( apiData->seq, &ev ); 1172 | if ( result == -ENOSPC ) { 1173 | std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; 1174 | continue; 1175 | } 1176 | else if ( result <= 0 ) { 1177 | std::cerr << "MidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; 1178 | continue; 1179 | } 1180 | 1181 | // This is a bit weird, but we now have to decode an ALSA MIDI 1182 | // event (back) into MIDI bytes. We'll ignore non-MIDI types. 1183 | if ( !continueSysex ) message.bytes.clear(); 1184 | 1185 | doDecode = false; 1186 | switch ( ev->type ) { 1187 | 1188 | case SND_SEQ_EVENT_PORT_SUBSCRIBED: 1189 | #if defined(__RTMIDI_DEBUG__) 1190 | std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n"; 1191 | #endif 1192 | break; 1193 | 1194 | case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: 1195 | #if defined(__RTMIDI_DEBUG__) 1196 | std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; 1197 | std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" 1198 | << (int) ev->data.connect.sender.port 1199 | << ", dest = " << (int) ev->data.connect.dest.client << ":" 1200 | << (int) ev->data.connect.dest.port 1201 | << std::endl; 1202 | #endif 1203 | break; 1204 | 1205 | case SND_SEQ_EVENT_QFRAME: // MIDI time code 1206 | if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; 1207 | break; 1208 | 1209 | case SND_SEQ_EVENT_TICK: // MIDI timing tick 1210 | if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; 1211 | break; 1212 | 1213 | case SND_SEQ_EVENT_SENSING: // Active sensing 1214 | if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; 1215 | break; 1216 | 1217 | case SND_SEQ_EVENT_SYSEX: 1218 | if ( (data->ignoreFlags & 0x01) ) break; 1219 | if ( ev->data.ext.len > apiData->bufferSize ) { 1220 | apiData->bufferSize = ev->data.ext.len; 1221 | free( buffer ); 1222 | buffer = (unsigned char *) malloc( apiData->bufferSize ); 1223 | if ( buffer == NULL ) { 1224 | data->doInput = false; 1225 | std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; 1226 | break; 1227 | } 1228 | } 1229 | 1230 | default: 1231 | doDecode = true; 1232 | } 1233 | 1234 | if ( doDecode ) { 1235 | 1236 | nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); 1237 | if ( nBytes > 0 ) { 1238 | // The ALSA sequencer has a maximum buffer size for MIDI sysex 1239 | // events of 256 bytes. If a device sends sysex messages larger 1240 | // than this, they are segmented into 256 byte chunks. So, 1241 | // we'll watch for this and concatenate sysex chunks into a 1242 | // single sysex message if necessary. 1243 | if ( !continueSysex ) 1244 | message.bytes.assign( buffer, &buffer[nBytes] ); 1245 | else 1246 | message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); 1247 | 1248 | continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); 1249 | if ( !continueSysex ) { 1250 | 1251 | // Calculate the time stamp: 1252 | message.timeStamp = 0.0; 1253 | 1254 | // Method 1: Use the system time. 1255 | //(void)gettimeofday(&tv, (struct timezone *)NULL); 1256 | //time = (tv.tv_sec * 1000000) + tv.tv_usec; 1257 | 1258 | // Method 2: Use the ALSA sequencer event time data. 1259 | // (thanks to Pedro Lopez-Cabanillas!). 1260 | time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); 1261 | lastTime = time; 1262 | time -= apiData->lastTime; 1263 | apiData->lastTime = lastTime; 1264 | if ( data->firstMessage == true ) 1265 | data->firstMessage = false; 1266 | else 1267 | message.timeStamp = time * 0.000001; 1268 | } 1269 | else { 1270 | #if defined(__RTMIDI_DEBUG__) 1271 | std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; 1272 | #endif 1273 | } 1274 | } 1275 | } 1276 | 1277 | snd_seq_free_event( ev ); 1278 | if ( message.bytes.size() == 0 || continueSysex ) continue; 1279 | 1280 | if ( data->usingCallback ) { 1281 | RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 1282 | callback( message.timeStamp, &message.bytes, data->userData ); 1283 | } 1284 | else { 1285 | // As long as we haven't reached our queue size limit, push the message. 1286 | if ( data->queue.size < data->queue.ringSize ) { 1287 | data->queue.ring[data->queue.back++] = message; 1288 | if ( data->queue.back == data->queue.ringSize ) 1289 | data->queue.back = 0; 1290 | data->queue.size++; 1291 | } 1292 | else 1293 | std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; 1294 | } 1295 | } 1296 | 1297 | if ( buffer ) free( buffer ); 1298 | snd_midi_event_free( apiData->coder ); 1299 | apiData->coder = 0; 1300 | apiData->thread = apiData->dummy_thread_id; 1301 | return 0; 1302 | } 1303 | 1304 | MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) 1305 | { 1306 | initialize( clientName ); 1307 | } 1308 | 1309 | MidiInAlsa :: ~MidiInAlsa() 1310 | { 1311 | // Close a connection if it exists. 1312 | closePort(); 1313 | 1314 | // Shutdown the input thread. 1315 | AlsaMidiData *data = static_cast (apiData_); 1316 | if ( inputData_.doInput ) { 1317 | inputData_.doInput = false; 1318 | int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) ); 1319 | (void) res; 1320 | if ( !pthread_equal(data->thread, data->dummy_thread_id) ) 1321 | pthread_join( data->thread, NULL ); 1322 | } 1323 | 1324 | // Cleanup. 1325 | close ( data->trigger_fds[0] ); 1326 | close ( data->trigger_fds[1] ); 1327 | if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); 1328 | #ifndef AVOID_TIMESTAMPING 1329 | snd_seq_free_queue( data->seq, data->queue_id ); 1330 | #endif 1331 | freeSequencer(); 1332 | delete data; 1333 | } 1334 | 1335 | void MidiInAlsa :: initialize( const std::string& clientName ) 1336 | { 1337 | snd_seq_t* seq = createSequencer( clientName ); 1338 | if ( seq == NULL ) { 1339 | s_seq = NULL; 1340 | errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; 1341 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1342 | } 1343 | 1344 | // Save our api-specific connection information. 1345 | AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; 1346 | data->seq = seq; 1347 | data->portNum = -1; 1348 | data->vport = -1; 1349 | data->subscription = 0; 1350 | data->dummy_thread_id = pthread_self(); 1351 | data->thread = data->dummy_thread_id; 1352 | data->trigger_fds[0] = -1; 1353 | data->trigger_fds[1] = -1; 1354 | apiData_ = (void *) data; 1355 | inputData_.apiData = (void *) data; 1356 | 1357 | if ( pipe(data->trigger_fds) == -1 ) { 1358 | errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; 1359 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1360 | } 1361 | 1362 | // Create the input queue 1363 | #ifndef AVOID_TIMESTAMPING 1364 | data->queue_id = snd_seq_alloc_named_queue(s_seq, "RtMidi Queue"); 1365 | // Set arbitrary tempo (mm=100) and resolution (240) 1366 | snd_seq_queue_tempo_t *qtempo; 1367 | snd_seq_queue_tempo_alloca(&qtempo); 1368 | snd_seq_queue_tempo_set_tempo(qtempo, 600000); 1369 | snd_seq_queue_tempo_set_ppq(qtempo, 240); 1370 | snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); 1371 | snd_seq_drain_output(data->seq); 1372 | #endif 1373 | } 1374 | 1375 | // This function is used to count or get the pinfo structure for a given port number. 1376 | unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) 1377 | { 1378 | snd_seq_client_info_t *cinfo; 1379 | int client; 1380 | int count = 0; 1381 | snd_seq_client_info_alloca( &cinfo ); 1382 | 1383 | snd_seq_client_info_set_client( cinfo, -1 ); 1384 | while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { 1385 | client = snd_seq_client_info_get_client( cinfo ); 1386 | if ( client == 0 ) continue; 1387 | // Reset query info 1388 | snd_seq_port_info_set_client( pinfo, client ); 1389 | snd_seq_port_info_set_port( pinfo, -1 ); 1390 | while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { 1391 | unsigned int atyp = snd_seq_port_info_get_type( pinfo ); 1392 | if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue; 1393 | unsigned int caps = snd_seq_port_info_get_capability( pinfo ); 1394 | if ( ( caps & type ) != type ) continue; 1395 | if ( count == portNumber ) return 1; 1396 | ++count; 1397 | } 1398 | } 1399 | 1400 | // If a negative portNumber was used, return the port count. 1401 | if ( portNumber < 0 ) return count; 1402 | return 0; 1403 | } 1404 | 1405 | unsigned int MidiInAlsa :: getPortCount() 1406 | { 1407 | snd_seq_port_info_t *pinfo; 1408 | snd_seq_port_info_alloca( &pinfo ); 1409 | 1410 | AlsaMidiData *data = static_cast (apiData_); 1411 | return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); 1412 | } 1413 | 1414 | std::string MidiInAlsa :: getPortName( unsigned int portNumber ) 1415 | { 1416 | snd_seq_client_info_t *cinfo; 1417 | snd_seq_port_info_t *pinfo; 1418 | snd_seq_client_info_alloca( &cinfo ); 1419 | snd_seq_port_info_alloca( &pinfo ); 1420 | 1421 | std::string stringName; 1422 | AlsaMidiData *data = static_cast (apiData_); 1423 | if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { 1424 | int cnum = snd_seq_port_info_get_client( pinfo ); 1425 | snd_seq_get_any_client_info( data->seq, cnum, cinfo ); 1426 | std::ostringstream os; 1427 | os << snd_seq_client_info_get_name( cinfo ); 1428 | os << " "; // GO: These lines added to make sure devices are listed 1429 | os << snd_seq_port_info_get_client( pinfo ); // GO: with full portnames added to ensure individual device names 1430 | os << ":"; 1431 | os << snd_seq_port_info_get_port( pinfo ); 1432 | stringName = os.str(); 1433 | return stringName; 1434 | } 1435 | 1436 | // If we get here, we didn't find a match. 1437 | errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; 1438 | RtMidi::error( RtError::WARNING, errorString_ ); 1439 | return stringName; 1440 | //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 1441 | } 1442 | 1443 | void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName ) 1444 | { 1445 | if ( connected_ ) { 1446 | errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; 1447 | RtMidi::error( RtError::WARNING, errorString_ ); 1448 | return; 1449 | } 1450 | 1451 | unsigned int nSrc = this->getPortCount(); 1452 | if (nSrc < 1) { 1453 | errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; 1454 | RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 1455 | } 1456 | 1457 | snd_seq_port_info_t *pinfo; 1458 | snd_seq_port_info_alloca( &pinfo ); 1459 | std::ostringstream ost; 1460 | AlsaMidiData *data = static_cast (apiData_); 1461 | if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { 1462 | ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 1463 | errorString_ = ost.str(); 1464 | RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 1465 | } 1466 | 1467 | 1468 | snd_seq_addr_t sender, receiver; 1469 | sender.client = snd_seq_port_info_get_client( pinfo ); 1470 | sender.port = snd_seq_port_info_get_port( pinfo ); 1471 | receiver.client = snd_seq_client_id( data->seq ); 1472 | if ( data->vport < 0 ) { 1473 | snd_seq_port_info_set_client( pinfo, 0 ); 1474 | snd_seq_port_info_set_port( pinfo, 0 ); 1475 | snd_seq_port_info_set_capability( pinfo, 1476 | SND_SEQ_PORT_CAP_WRITE | 1477 | SND_SEQ_PORT_CAP_SUBS_WRITE ); 1478 | snd_seq_port_info_set_type( pinfo, 1479 | SND_SEQ_PORT_TYPE_MIDI_GENERIC | 1480 | SND_SEQ_PORT_TYPE_APPLICATION ); 1481 | snd_seq_port_info_set_midi_channels(pinfo, 16); 1482 | #ifndef AVOID_TIMESTAMPING 1483 | snd_seq_port_info_set_timestamping(pinfo, 1); 1484 | snd_seq_port_info_set_timestamp_real(pinfo, 1); 1485 | snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); 1486 | #endif 1487 | snd_seq_port_info_set_name(pinfo, portName.c_str() ); 1488 | data->vport = snd_seq_create_port(data->seq, pinfo); 1489 | 1490 | if ( data->vport < 0 ) { 1491 | errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; 1492 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1493 | } 1494 | } 1495 | 1496 | receiver.port = data->vport; 1497 | 1498 | if ( !data->subscription ) { 1499 | // Make subscription 1500 | if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) { 1501 | errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription."; 1502 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1503 | } 1504 | snd_seq_port_subscribe_set_sender(data->subscription, &sender); 1505 | snd_seq_port_subscribe_set_dest(data->subscription, &receiver); 1506 | if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { 1507 | snd_seq_port_subscribe_free( data->subscription ); 1508 | data->subscription = 0; 1509 | errorString_ = "MidiInAlsa::openPort: ALSA error making port connection."; 1510 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1511 | } 1512 | } 1513 | 1514 | if ( inputData_.doInput == false ) { 1515 | // Start the input queue 1516 | #ifndef AVOID_TIMESTAMPING 1517 | snd_seq_start_queue( data->seq, data->queue_id, NULL ); 1518 | snd_seq_drain_output( data->seq ); 1519 | #endif 1520 | // Start our MIDI input thread. 1521 | pthread_attr_t attr; 1522 | pthread_attr_init(&attr); 1523 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 1524 | pthread_attr_setschedpolicy(&attr, SCHED_OTHER); 1525 | 1526 | inputData_.doInput = true; 1527 | int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); 1528 | pthread_attr_destroy(&attr); 1529 | if ( err ) { 1530 | snd_seq_unsubscribe_port( data->seq, data->subscription ); 1531 | snd_seq_port_subscribe_free( data->subscription ); 1532 | data->subscription = 0; 1533 | inputData_.doInput = false; 1534 | errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; 1535 | RtMidi::error( RtError::THREAD_ERROR, errorString_ ); 1536 | } 1537 | } 1538 | 1539 | connected_ = true; 1540 | } 1541 | 1542 | void MidiInAlsa :: openVirtualPort( std::string portName ) 1543 | { 1544 | AlsaMidiData *data = static_cast (apiData_); 1545 | if ( data->vport < 0 ) { 1546 | snd_seq_port_info_t *pinfo; 1547 | snd_seq_port_info_alloca( &pinfo ); 1548 | snd_seq_port_info_set_capability( pinfo, 1549 | SND_SEQ_PORT_CAP_WRITE | 1550 | SND_SEQ_PORT_CAP_SUBS_WRITE ); 1551 | snd_seq_port_info_set_type( pinfo, 1552 | SND_SEQ_PORT_TYPE_MIDI_GENERIC | 1553 | SND_SEQ_PORT_TYPE_APPLICATION ); 1554 | snd_seq_port_info_set_midi_channels(pinfo, 16); 1555 | #ifndef AVOID_TIMESTAMPING 1556 | snd_seq_port_info_set_timestamping(pinfo, 1); 1557 | snd_seq_port_info_set_timestamp_real(pinfo, 1); 1558 | snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); 1559 | #endif 1560 | snd_seq_port_info_set_name(pinfo, portName.c_str()); 1561 | data->vport = snd_seq_create_port(data->seq, pinfo); 1562 | 1563 | if ( data->vport < 0 ) { 1564 | errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port."; 1565 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1566 | } 1567 | } 1568 | 1569 | if ( inputData_.doInput == false ) { 1570 | // Wait for old thread to stop, if still running 1571 | if ( !pthread_equal(data->thread, data->dummy_thread_id) ) 1572 | pthread_join( data->thread, NULL ); 1573 | 1574 | // Start the input queue 1575 | #ifndef AVOID_TIMESTAMPING 1576 | snd_seq_start_queue( data->seq, data->queue_id, NULL ); 1577 | snd_seq_drain_output( data->seq ); 1578 | #endif 1579 | // Start our MIDI input thread. 1580 | pthread_attr_t attr; 1581 | pthread_attr_init(&attr); 1582 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 1583 | pthread_attr_setschedpolicy(&attr, SCHED_OTHER); 1584 | 1585 | inputData_.doInput = true; 1586 | int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); 1587 | pthread_attr_destroy(&attr); 1588 | if ( err ) { 1589 | if ( data->subscription ) { 1590 | snd_seq_unsubscribe_port( data->seq, data->subscription ); 1591 | snd_seq_port_subscribe_free( data->subscription ); 1592 | data->subscription = 0; 1593 | } 1594 | inputData_.doInput = false; 1595 | errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; 1596 | RtMidi::error( RtError::THREAD_ERROR, errorString_ ); 1597 | } 1598 | } 1599 | } 1600 | 1601 | void MidiInAlsa :: closePort( void ) 1602 | { 1603 | AlsaMidiData *data = static_cast (apiData_); 1604 | 1605 | if ( connected_ ) { 1606 | if ( data->subscription ) { 1607 | snd_seq_unsubscribe_port( data->seq, data->subscription ); 1608 | snd_seq_port_subscribe_free( data->subscription ); 1609 | data->subscription = 0; 1610 | } 1611 | // Stop the input queue 1612 | #ifndef AVOID_TIMESTAMPING 1613 | snd_seq_stop_queue( data->seq, data->queue_id, NULL ); 1614 | snd_seq_drain_output( data->seq ); 1615 | #endif 1616 | connected_ = false; 1617 | } 1618 | 1619 | // Stop thread to avoid triggering the callback, while the port is intended to be closed 1620 | if ( inputData_.doInput ) { 1621 | inputData_.doInput = false; 1622 | int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) ); 1623 | (void) res; 1624 | if ( !pthread_equal(data->thread, data->dummy_thread_id) ) 1625 | pthread_join( data->thread, NULL ); 1626 | } 1627 | } 1628 | 1629 | //*********************************************************************// 1630 | // API: LINUX ALSA 1631 | // Class Definitions: MidiOutAlsa 1632 | //*********************************************************************// 1633 | 1634 | MidiOutAlsa :: MidiOutAlsa( const std::string clientName ) : MidiOutApi() 1635 | { 1636 | initialize( clientName ); 1637 | } 1638 | 1639 | MidiOutAlsa :: ~MidiOutAlsa() 1640 | { 1641 | // Close a connection if it exists. 1642 | closePort(); 1643 | 1644 | // Cleanup. 1645 | AlsaMidiData *data = static_cast (apiData_); 1646 | if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); 1647 | if ( data->coder ) snd_midi_event_free( data->coder ); 1648 | if ( data->buffer ) free( data->buffer ); 1649 | freeSequencer(); 1650 | delete data; 1651 | } 1652 | 1653 | void MidiOutAlsa :: initialize( const std::string& clientName ) 1654 | { 1655 | snd_seq_t* seq = createSequencer( clientName ); 1656 | if ( seq == NULL ) { 1657 | s_seq = NULL; 1658 | errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object."; 1659 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1660 | } 1661 | 1662 | // Save our api-specific connection information. 1663 | AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; 1664 | data->seq = seq; 1665 | data->portNum = -1; 1666 | data->vport = -1; 1667 | data->bufferSize = 32; 1668 | data->coder = 0; 1669 | data->buffer = 0; 1670 | int result = snd_midi_event_new( data->bufferSize, &data->coder ); 1671 | if ( result < 0 ) { 1672 | delete data; 1673 | errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n"; 1674 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1675 | } 1676 | data->buffer = (unsigned char *) malloc( data->bufferSize ); 1677 | if ( data->buffer == NULL ) { 1678 | delete data; 1679 | errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; 1680 | RtMidi::error( RtError::MEMORY_ERROR, errorString_ ); 1681 | } 1682 | snd_midi_event_init( data->coder ); 1683 | apiData_ = (void *) data; 1684 | } 1685 | 1686 | unsigned int MidiOutAlsa :: getPortCount() 1687 | { 1688 | snd_seq_port_info_t *pinfo; 1689 | snd_seq_port_info_alloca( &pinfo ); 1690 | 1691 | AlsaMidiData *data = static_cast (apiData_); 1692 | return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); 1693 | } 1694 | 1695 | std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) 1696 | { 1697 | snd_seq_client_info_t *cinfo; 1698 | snd_seq_port_info_t *pinfo; 1699 | snd_seq_client_info_alloca( &cinfo ); 1700 | snd_seq_port_info_alloca( &pinfo ); 1701 | 1702 | std::string stringName; 1703 | AlsaMidiData *data = static_cast (apiData_); 1704 | if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { 1705 | int cnum = snd_seq_port_info_get_client(pinfo); 1706 | snd_seq_get_any_client_info( data->seq, cnum, cinfo ); 1707 | std::ostringstream os; 1708 | os << snd_seq_client_info_get_name(cinfo); 1709 | os << ":"; 1710 | os << snd_seq_port_info_get_port(pinfo); 1711 | stringName = os.str(); 1712 | return stringName; 1713 | } 1714 | 1715 | // If we get here, we didn't find a match. 1716 | errorString_ = "MidiOutAlsa::getPortName: error looking for port name!"; 1717 | //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 1718 | RtMidi::error( RtError::WARNING, errorString_ ); 1719 | return stringName; 1720 | } 1721 | 1722 | void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string portName ) 1723 | { 1724 | if ( connected_ ) { 1725 | errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; 1726 | RtMidi::error( RtError::WARNING, errorString_ ); 1727 | return; 1728 | } 1729 | 1730 | unsigned int nSrc = this->getPortCount(); 1731 | if (nSrc < 1) { 1732 | errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!"; 1733 | RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 1734 | } 1735 | 1736 | snd_seq_port_info_t *pinfo; 1737 | snd_seq_port_info_alloca( &pinfo ); 1738 | std::ostringstream ost; 1739 | AlsaMidiData *data = static_cast (apiData_); 1740 | if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { 1741 | ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 1742 | errorString_ = ost.str(); 1743 | RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 1744 | } 1745 | 1746 | snd_seq_addr_t sender, receiver; 1747 | receiver.client = snd_seq_port_info_get_client( pinfo ); 1748 | receiver.port = snd_seq_port_info_get_port( pinfo ); 1749 | sender.client = snd_seq_client_id( data->seq ); 1750 | 1751 | if ( data->vport < 0 ) { 1752 | data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), 1753 | SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, 1754 | SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); 1755 | if ( data->vport < 0 ) { 1756 | errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port."; 1757 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1758 | } 1759 | } 1760 | 1761 | sender.port = data->vport; 1762 | 1763 | // Make subscription 1764 | if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) { 1765 | snd_seq_port_subscribe_free( data->subscription ); 1766 | errorString_ = "MidiOutAlsa::openPort: error allocation port subscribtion."; 1767 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1768 | } 1769 | snd_seq_port_subscribe_set_sender(data->subscription, &sender); 1770 | snd_seq_port_subscribe_set_dest(data->subscription, &receiver); 1771 | snd_seq_port_subscribe_set_time_update(data->subscription, 1); 1772 | snd_seq_port_subscribe_set_time_real(data->subscription, 1); 1773 | if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { 1774 | snd_seq_port_subscribe_free( data->subscription ); 1775 | errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection."; 1776 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1777 | } 1778 | 1779 | connected_ = true; 1780 | } 1781 | 1782 | void MidiOutAlsa :: closePort( void ) 1783 | { 1784 | if ( connected_ ) { 1785 | AlsaMidiData *data = static_cast (apiData_); 1786 | snd_seq_unsubscribe_port( data->seq, data->subscription ); 1787 | snd_seq_port_subscribe_free( data->subscription ); 1788 | connected_ = false; 1789 | } 1790 | } 1791 | 1792 | void MidiOutAlsa :: openVirtualPort( std::string portName ) 1793 | { 1794 | AlsaMidiData *data = static_cast (apiData_); 1795 | if ( data->vport < 0 ) { 1796 | data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), 1797 | SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, 1798 | SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); 1799 | 1800 | if ( data->vport < 0 ) { 1801 | errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port."; 1802 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1803 | } 1804 | } 1805 | } 1806 | 1807 | void MidiOutAlsa :: sendMessage( std::vector *message ) 1808 | { 1809 | int result; 1810 | AlsaMidiData *data = static_cast (apiData_); 1811 | unsigned int nBytes = message->size(); 1812 | if ( nBytes > data->bufferSize ) { 1813 | data->bufferSize = nBytes; 1814 | result = snd_midi_event_resize_buffer ( data->coder, nBytes); 1815 | if ( result != 0 ) { 1816 | errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer."; 1817 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 1818 | } 1819 | free (data->buffer); 1820 | data->buffer = (unsigned char *) malloc( data->bufferSize ); 1821 | if ( data->buffer == NULL ) { 1822 | errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; 1823 | RtMidi::error( RtError::MEMORY_ERROR, errorString_ ); 1824 | } 1825 | } 1826 | 1827 | snd_seq_event_t ev; 1828 | snd_seq_ev_clear(&ev); 1829 | snd_seq_ev_set_source(&ev, data->vport); 1830 | snd_seq_ev_set_subs(&ev); 1831 | snd_seq_ev_set_direct(&ev); 1832 | for ( unsigned int i=0; ibuffer[i] = message->at(i); 1833 | result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); 1834 | if ( result < (int)nBytes ) { 1835 | errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; 1836 | RtMidi::error( RtError::WARNING, errorString_ ); 1837 | return; 1838 | } 1839 | 1840 | // Send the event. 1841 | result = snd_seq_event_output(data->seq, &ev); 1842 | if ( result < 0 ) { 1843 | errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port."; 1844 | RtMidi::error( RtError::WARNING, errorString_ ); 1845 | } 1846 | snd_seq_drain_output(data->seq); 1847 | } 1848 | 1849 | #endif // __LINUX_ALSA__ 1850 | 1851 | 1852 | //*********************************************************************// 1853 | // API: Windows Multimedia Library (MM) 1854 | //*********************************************************************// 1855 | 1856 | // API information deciphered from: 1857 | // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp 1858 | 1859 | // Thanks to Jean-Baptiste Berruchon for the sysex code. 1860 | 1861 | #if defined(__WINDOWS_MM__) 1862 | 1863 | // The Windows MM API is based on the use of a callback function for 1864 | // MIDI input. We convert the system specific time stamps to delta 1865 | // time values. 1866 | 1867 | // Windows MM MIDI header files. 1868 | #include 1869 | #include 1870 | 1871 | #define RT_SYSEX_BUFFER_SIZE 1024 1872 | #define RT_SYSEX_BUFFER_COUNT 4 1873 | 1874 | // A structure to hold variables related to the CoreMIDI API 1875 | // implementation. 1876 | struct WinMidiData { 1877 | HMIDIIN inHandle; // Handle to Midi Input Device 1878 | HMIDIOUT outHandle; // Handle to Midi Output Device 1879 | DWORD lastTime; 1880 | MidiInApi::MidiMessage message; 1881 | LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT]; 1882 | }; 1883 | 1884 | //*********************************************************************// 1885 | // API: Windows MM 1886 | // Class Definitions: MidiInWinMM 1887 | //*********************************************************************// 1888 | 1889 | static void CALLBACK midiInputCallback( HMIDIIN hmin, 1890 | UINT inputStatus, 1891 | DWORD_PTR instancePtr, 1892 | DWORD_PTR midiMessage, 1893 | DWORD timestamp ) 1894 | { 1895 | if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; 1896 | 1897 | //MidiInApi::RtMidiInData *data = static_cast (instancePtr); 1898 | MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; 1899 | WinMidiData *apiData = static_cast (data->apiData); 1900 | 1901 | // Calculate time stamp. 1902 | if ( data->firstMessage == true ) { 1903 | apiData->message.timeStamp = 0.0; 1904 | data->firstMessage = false; 1905 | } 1906 | else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; 1907 | apiData->lastTime = timestamp; 1908 | 1909 | if ( inputStatus == MIM_DATA ) { // Channel or system message 1910 | 1911 | // Make sure the first byte is a status byte. 1912 | unsigned char status = (unsigned char) (midiMessage & 0x000000FF); 1913 | if ( !(status & 0x80) ) return; 1914 | 1915 | // Determine the number of bytes in the MIDI message. 1916 | unsigned short nBytes = 1; 1917 | if ( status < 0xC0 ) nBytes = 3; 1918 | else if ( status < 0xE0 ) nBytes = 2; 1919 | else if ( status < 0xF0 ) nBytes = 3; 1920 | else if ( status == 0xF1 ) { 1921 | if ( data->ignoreFlags & 0x02 ) return; 1922 | else nBytes = 2; 1923 | } 1924 | else if ( status == 0xF2 ) nBytes = 3; 1925 | else if ( status == 0xF3 ) nBytes = 2; 1926 | else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) { 1927 | // A MIDI timing tick message and we're ignoring it. 1928 | return; 1929 | } 1930 | else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { 1931 | // A MIDI active sensing message and we're ignoring it. 1932 | return; 1933 | } 1934 | 1935 | // Copy bytes to our MIDI message. 1936 | unsigned char *ptr = (unsigned char *) &midiMessage; 1937 | for ( int i=0; imessage.bytes.push_back( *ptr++ ); 1938 | } 1939 | else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) 1940 | MIDIHDR *sysex = ( MIDIHDR *) midiMessage; 1941 | if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { 1942 | // Sysex message and we're not ignoring it 1943 | for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) 1944 | apiData->message.bytes.push_back( sysex->lpData[i] ); 1945 | } 1946 | 1947 | // The WinMM API requires that the sysex buffer be requeued after 1948 | // input of each sysex message. Even if we are ignoring sysex 1949 | // messages, we still need to requeue the buffer in case the user 1950 | // decides to not ignore sysex messages in the future. However, 1951 | // it seems that WinMM calls this function with an empty sysex 1952 | // buffer when an application closes and in this case, we should 1953 | // avoid requeueing it, else the computer suddenly reboots after 1954 | // one or two minutes. 1955 | if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { 1956 | //if ( sysex->dwBytesRecorded > 0 ) { 1957 | MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); 1958 | if ( result != MMSYSERR_NOERROR ) 1959 | std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; 1960 | 1961 | if ( data->ignoreFlags & 0x01 ) return; 1962 | } 1963 | else return; 1964 | } 1965 | 1966 | if ( data->usingCallback ) { 1967 | RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; 1968 | callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); 1969 | } 1970 | else { 1971 | // As long as we haven't reached our queue size limit, push the message. 1972 | if ( data->queue.size < data->queue.ringSize ) { 1973 | data->queue.ring[data->queue.back++] = apiData->message; 1974 | if ( data->queue.back == data->queue.ringSize ) 1975 | data->queue.back = 0; 1976 | data->queue.size++; 1977 | } 1978 | else 1979 | std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; 1980 | } 1981 | 1982 | // Clear the vector for the next input message. 1983 | apiData->message.bytes.clear(); 1984 | } 1985 | 1986 | MidiInWinMM :: MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) 1987 | { 1988 | initialize( clientName ); 1989 | } 1990 | 1991 | MidiInWinMM :: ~MidiInWinMM() 1992 | { 1993 | // Close a connection if it exists. 1994 | closePort(); 1995 | 1996 | // Cleanup. 1997 | WinMidiData *data = static_cast (apiData_); 1998 | delete data; 1999 | } 2000 | 2001 | void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) 2002 | { 2003 | // We'll issue a warning here if no devices are available but not 2004 | // throw an error since the user can plugin something later. 2005 | unsigned int nDevices = midiInGetNumDevs(); 2006 | if ( nDevices == 0 ) { 2007 | errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available."; 2008 | RtMidi::error( RtError::WARNING, errorString_ ); 2009 | } 2010 | 2011 | // Save our api-specific connection information. 2012 | WinMidiData *data = (WinMidiData *) new WinMidiData; 2013 | apiData_ = (void *) data; 2014 | inputData_.apiData = (void *) data; 2015 | data->message.bytes.clear(); // needs to be empty for first input message 2016 | } 2017 | 2018 | void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ ) 2019 | { 2020 | if ( connected_ ) { 2021 | errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; 2022 | RtMidi::error( RtError::WARNING, errorString_ ); 2023 | return; 2024 | } 2025 | 2026 | unsigned int nDevices = midiInGetNumDevs(); 2027 | if (nDevices == 0) { 2028 | errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!"; 2029 | RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 2030 | } 2031 | 2032 | std::ostringstream ost; 2033 | if ( portNumber >= nDevices ) { 2034 | ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 2035 | errorString_ = ost.str(); 2036 | RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 2037 | } 2038 | 2039 | WinMidiData *data = static_cast (apiData_); 2040 | MMRESULT result = midiInOpen( &data->inHandle, 2041 | portNumber, 2042 | (DWORD_PTR)&midiInputCallback, 2043 | (DWORD_PTR)&inputData_, 2044 | CALLBACK_FUNCTION ); 2045 | if ( result != MMSYSERR_NOERROR ) { 2046 | errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port."; 2047 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2048 | } 2049 | 2050 | // Allocate and init the sysex buffers. 2051 | for ( int i=0; isysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; 2053 | data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; 2054 | data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE; 2055 | data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator 2056 | data->sysexBuffer[i]->dwFlags = 0; 2057 | 2058 | result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); 2059 | if ( result != MMSYSERR_NOERROR ) { 2060 | midiInClose( data->inHandle ); 2061 | errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; 2062 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2063 | } 2064 | 2065 | // Register the buffer. 2066 | result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); 2067 | if ( result != MMSYSERR_NOERROR ) { 2068 | midiInClose( data->inHandle ); 2069 | errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; 2070 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2071 | } 2072 | } 2073 | 2074 | result = midiInStart( data->inHandle ); 2075 | if ( result != MMSYSERR_NOERROR ) { 2076 | midiInClose( data->inHandle ); 2077 | errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; 2078 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2079 | } 2080 | 2081 | connected_ = true; 2082 | } 2083 | 2084 | void MidiInWinMM :: openVirtualPort( std::string portName ) 2085 | { 2086 | // This function cannot be implemented for the Windows MM MIDI API. 2087 | errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; 2088 | RtMidi::error( RtError::WARNING, errorString_ ); 2089 | } 2090 | 2091 | void MidiInWinMM :: closePort( void ) 2092 | { 2093 | if ( connected_ ) { 2094 | WinMidiData *data = static_cast (apiData_); 2095 | midiInReset( data->inHandle ); 2096 | midiInStop( data->inHandle ); 2097 | 2098 | for ( int i=0; iinHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); 2100 | delete [] data->sysexBuffer[i]->lpData; 2101 | delete [] data->sysexBuffer[i]; 2102 | if ( result != MMSYSERR_NOERROR ) { 2103 | midiInClose( data->inHandle ); 2104 | errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; 2105 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2106 | } 2107 | } 2108 | 2109 | midiInClose( data->inHandle ); 2110 | connected_ = false; 2111 | } 2112 | } 2113 | 2114 | unsigned int MidiInWinMM :: getPortCount() 2115 | { 2116 | return midiInGetNumDevs(); 2117 | } 2118 | 2119 | std::string MidiInWinMM :: getPortName( unsigned int portNumber ) 2120 | { 2121 | std::string stringName; 2122 | unsigned int nDevices = midiInGetNumDevs(); 2123 | if ( portNumber >= nDevices ) { 2124 | std::ostringstream ost; 2125 | ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 2126 | errorString_ = ost.str(); 2127 | //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 2128 | RtMidi::error( RtError::WARNING, errorString_ ); 2129 | return stringName; 2130 | } 2131 | 2132 | MIDIINCAPS deviceCaps; 2133 | midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); 2134 | 2135 | #if defined( UNICODE ) || defined( _UNICODE ) 2136 | int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL); 2137 | stringName.assign( length, 0 ); 2138 | length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, wcslen(deviceCaps.szPname), &stringName[0], length, NULL, NULL); 2139 | #else 2140 | stringName = std::string( deviceCaps.szPname ); 2141 | #endif 2142 | 2143 | // Next lines added to add the portNumber to the name so that 2144 | // the device's names are sure to be listed with individual names 2145 | // even when they have the same brand name 2146 | std::ostringstream os; 2147 | os << " "; 2148 | os << portNumber; 2149 | stringName += os.str(); 2150 | 2151 | return stringName; 2152 | } 2153 | 2154 | //*********************************************************************// 2155 | // API: Windows MM 2156 | // Class Definitions: MidiOutWinMM 2157 | //*********************************************************************// 2158 | 2159 | MidiOutWinMM :: MidiOutWinMM( const std::string clientName ) : MidiOutApi() 2160 | { 2161 | initialize( clientName ); 2162 | } 2163 | 2164 | MidiOutWinMM :: ~MidiOutWinMM() 2165 | { 2166 | // Close a connection if it exists. 2167 | closePort(); 2168 | 2169 | // Cleanup. 2170 | WinMidiData *data = static_cast (apiData_); 2171 | delete data; 2172 | } 2173 | 2174 | void MidiOutWinMM :: initialize( const std::string& /*clientName*/ ) 2175 | { 2176 | // We'll issue a warning here if no devices are available but not 2177 | // throw an error since the user can plug something in later. 2178 | unsigned int nDevices = midiOutGetNumDevs(); 2179 | if ( nDevices == 0 ) { 2180 | errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available."; 2181 | RtMidi::error( RtError::WARNING, errorString_ ); 2182 | } 2183 | 2184 | // Save our api-specific connection information. 2185 | WinMidiData *data = (WinMidiData *) new WinMidiData; 2186 | apiData_ = (void *) data; 2187 | } 2188 | 2189 | unsigned int MidiOutWinMM :: getPortCount() 2190 | { 2191 | return midiOutGetNumDevs(); 2192 | } 2193 | 2194 | std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) 2195 | { 2196 | std::string stringName; 2197 | unsigned int nDevices = midiOutGetNumDevs(); 2198 | if ( portNumber >= nDevices ) { 2199 | std::ostringstream ost; 2200 | ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 2201 | errorString_ = ost.str(); 2202 | //RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 2203 | RtMidi::error( RtError::WARNING, errorString_ ); 2204 | return stringName; 2205 | } 2206 | 2207 | MIDIOUTCAPS deviceCaps; 2208 | midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); 2209 | 2210 | #if defined( UNICODE ) || defined( _UNICODE ) 2211 | int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL); 2212 | stringName.assign( length, 0 ); 2213 | length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, wcslen(deviceCaps.szPname), &stringName[0], length, NULL, NULL); 2214 | #else 2215 | stringName = std::string( deviceCaps.szPname ); 2216 | #endif 2217 | 2218 | return stringName; 2219 | } 2220 | 2221 | void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ ) 2222 | { 2223 | if ( connected_ ) { 2224 | errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; 2225 | RtMidi::error( RtError::WARNING, errorString_ ); 2226 | return; 2227 | } 2228 | 2229 | unsigned int nDevices = midiOutGetNumDevs(); 2230 | if (nDevices < 1) { 2231 | errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!"; 2232 | RtMidi::error( RtError::NO_DEVICES_FOUND, errorString_ ); 2233 | } 2234 | 2235 | std::ostringstream ost; 2236 | if ( portNumber >= nDevices ) { 2237 | ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 2238 | errorString_ = ost.str(); 2239 | RtMidi::error( RtError::INVALID_PARAMETER, errorString_ ); 2240 | } 2241 | 2242 | WinMidiData *data = static_cast (apiData_); 2243 | MMRESULT result = midiOutOpen( &data->outHandle, 2244 | portNumber, 2245 | (DWORD)NULL, 2246 | (DWORD)NULL, 2247 | CALLBACK_NULL ); 2248 | if ( result != MMSYSERR_NOERROR ) { 2249 | errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port."; 2250 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2251 | } 2252 | 2253 | connected_ = true; 2254 | } 2255 | 2256 | void MidiOutWinMM :: closePort( void ) 2257 | { 2258 | if ( connected_ ) { 2259 | WinMidiData *data = static_cast (apiData_); 2260 | midiOutReset( data->outHandle ); 2261 | midiOutClose( data->outHandle ); 2262 | connected_ = false; 2263 | } 2264 | } 2265 | 2266 | void MidiOutWinMM :: openVirtualPort( std::string portName ) 2267 | { 2268 | // This function cannot be implemented for the Windows MM MIDI API. 2269 | errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; 2270 | RtMidi::error( RtError::WARNING, errorString_ ); 2271 | } 2272 | 2273 | void MidiOutWinMM :: sendMessage( std::vector *message ) 2274 | { 2275 | unsigned int nBytes = static_cast(message->size()); 2276 | if ( nBytes == 0 ) { 2277 | errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; 2278 | RtMidi::error( RtError::WARNING, errorString_ ); 2279 | return; 2280 | } 2281 | 2282 | MMRESULT result; 2283 | WinMidiData *data = static_cast (apiData_); 2284 | if ( message->at(0) == 0xF0 ) { // Sysex message 2285 | 2286 | // Allocate buffer for sysex data. 2287 | char *buffer = (char *) malloc( nBytes ); 2288 | if ( buffer == NULL ) { 2289 | errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!"; 2290 | RtMidi::error( RtError::MEMORY_ERROR, errorString_ ); 2291 | } 2292 | 2293 | // Copy data to buffer. 2294 | for ( unsigned int i=0; iat(i); 2295 | 2296 | // Create and prepare MIDIHDR structure. 2297 | MIDIHDR sysex; 2298 | sysex.lpData = (LPSTR) buffer; 2299 | sysex.dwBufferLength = nBytes; 2300 | sysex.dwFlags = 0; 2301 | result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) ); 2302 | if ( result != MMSYSERR_NOERROR ) { 2303 | free( buffer ); 2304 | errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header."; 2305 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2306 | } 2307 | 2308 | // Send the message. 2309 | result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); 2310 | if ( result != MMSYSERR_NOERROR ) { 2311 | free( buffer ); 2312 | errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message."; 2313 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2314 | } 2315 | 2316 | // Unprepare the buffer and MIDIHDR. 2317 | while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); 2318 | free( buffer ); 2319 | 2320 | } 2321 | else { // Channel or system message. 2322 | 2323 | // Make sure the message size isn't too big. 2324 | if ( nBytes > 3 ) { 2325 | errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!"; 2326 | RtMidi::error( RtError::WARNING, errorString_ ); 2327 | return; 2328 | } 2329 | 2330 | // Pack MIDI bytes into double word. 2331 | DWORD packet; 2332 | unsigned char *ptr = (unsigned char *) &packet; 2333 | for ( unsigned int i=0; iat(i); 2335 | ++ptr; 2336 | } 2337 | 2338 | // Send the message immediately. 2339 | result = midiOutShortMsg( data->outHandle, packet ); 2340 | if ( result != MMSYSERR_NOERROR ) { 2341 | errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message."; 2342 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 2343 | } 2344 | } 2345 | } 2346 | 2347 | #endif // __WINDOWS_MM__ 2348 | 2349 | // *********************************************************************// 2350 | // API: WINDOWS Kernel Streaming 2351 | // 2352 | // Written by Sebastien Alaiwan, 2012. 2353 | // 2354 | // NOTE BY GARY: much of the KS-specific code below probably should go in a separate file. 2355 | // 2356 | // *********************************************************************// 2357 | 2358 | #if defined(__WINDOWS_KS__) 2359 | 2360 | #include 2361 | #include 2362 | #include 2363 | #include 2364 | #include 2365 | #include 2366 | #include 2367 | #include 2368 | 2369 | #include "ks.h" 2370 | #include "ksmedia.h" 2371 | 2372 | #define INSTANTIATE_GUID(a) GUID const a = { STATIC_ ## a } 2373 | 2374 | INSTANTIATE_GUID(GUID_NULL); 2375 | INSTANTIATE_GUID(KSPROPSETID_Pin); 2376 | INSTANTIATE_GUID(KSPROPSETID_Connection); 2377 | INSTANTIATE_GUID(KSPROPSETID_Topology); 2378 | INSTANTIATE_GUID(KSINTERFACESETID_Standard); 2379 | INSTANTIATE_GUID(KSMEDIUMSETID_Standard); 2380 | INSTANTIATE_GUID(KSDATAFORMAT_TYPE_MUSIC); 2381 | INSTANTIATE_GUID(KSDATAFORMAT_SUBTYPE_MIDI); 2382 | INSTANTIATE_GUID(KSDATAFORMAT_SPECIFIER_NONE); 2383 | 2384 | #undef INSTANTIATE_GUID 2385 | 2386 | typedef std::basic_string tstring; 2387 | 2388 | inline bool IsValid(HANDLE handle) 2389 | { 2390 | return handle != NULL && handle != INVALID_HANDLE_VALUE; 2391 | } 2392 | 2393 | class ComException : public std::runtime_error 2394 | { 2395 | private: 2396 | static std::string MakeString(std::string const& s, HRESULT hr) 2397 | { 2398 | std::stringstream ss; 2399 | ss << "(error 0x" << std::hex << hr << ")"; 2400 | return s + ss.str(); 2401 | } 2402 | 2403 | public: 2404 | ComException(std::string const& s, HRESULT hr) : 2405 | std::runtime_error(MakeString(s, hr)) 2406 | { 2407 | } 2408 | }; 2409 | 2410 | template 2411 | class CKsEnumFilters 2412 | { 2413 | public: 2414 | ~CKsEnumFilters() 2415 | { 2416 | DestroyLists(); 2417 | } 2418 | 2419 | void EnumFilters(GUID const* categories, size_t numCategories) 2420 | { 2421 | DestroyLists(); 2422 | 2423 | if (categories == 0) 2424 | throw std::runtime_error("CKsEnumFilters: invalid argument"); 2425 | 2426 | // Get a handle to the device set specified by the guid 2427 | HDEVINFO hDevInfo = ::SetupDiGetClassDevs(&categories[0], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 2428 | if (!IsValid(hDevInfo)) 2429 | throw std::runtime_error("CKsEnumFilters: no devices found"); 2430 | 2431 | // Loop through members of the set and get details for each 2432 | for (int iClassMember=0;;iClassMember++) { 2433 | try { 2434 | SP_DEVICE_INTERFACE_DATA DID; 2435 | DID.cbSize = sizeof(DID); 2436 | DID.Reserved = 0; 2437 | 2438 | bool fRes = ::SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &categories[0], iClassMember, &DID); 2439 | if (!fRes) 2440 | break; 2441 | 2442 | // Get filter friendly name 2443 | HKEY hRegKey = ::SetupDiOpenDeviceInterfaceRegKey(hDevInfo, &DID, 0, KEY_READ); 2444 | if (hRegKey == INVALID_HANDLE_VALUE) 2445 | throw std::runtime_error("CKsEnumFilters: interface has no registry"); 2446 | 2447 | char friendlyName[256]; 2448 | DWORD dwSize = sizeof friendlyName; 2449 | LONG lval = ::RegQueryValueEx(hRegKey, TEXT("FriendlyName"), NULL, NULL, (LPBYTE)friendlyName, &dwSize); 2450 | ::RegCloseKey(hRegKey); 2451 | if (lval != ERROR_SUCCESS) 2452 | throw std::runtime_error("CKsEnumFilters: interface has no friendly name"); 2453 | 2454 | // Get details for the device registered in this class 2455 | DWORD const cbItfDetails = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + MAX_PATH * sizeof(WCHAR); 2456 | std::vector buffer(cbItfDetails); 2457 | 2458 | SP_DEVICE_INTERFACE_DETAIL_DATA* pDevInterfaceDetails = reinterpret_cast(&buffer[0]); 2459 | pDevInterfaceDetails->cbSize = sizeof(*pDevInterfaceDetails); 2460 | 2461 | SP_DEVINFO_DATA DevInfoData; 2462 | DevInfoData.cbSize = sizeof(DevInfoData); 2463 | DevInfoData.Reserved = 0; 2464 | 2465 | fRes = ::SetupDiGetDeviceInterfaceDetail(hDevInfo, &DID, pDevInterfaceDetails, cbItfDetails, NULL, &DevInfoData); 2466 | if (!fRes) 2467 | throw std::runtime_error("CKsEnumFilters: could not get interface details"); 2468 | 2469 | // check additional category guids which may (or may not) have been supplied 2470 | for (size_t i=1; i < numCategories; ++i) { 2471 | SP_DEVICE_INTERFACE_DATA DIDAlias; 2472 | DIDAlias.cbSize = sizeof(DIDAlias); 2473 | DIDAlias.Reserved = 0; 2474 | 2475 | fRes = ::SetupDiGetDeviceInterfaceAlias(hDevInfo, &DID, &categories[i], &DIDAlias); 2476 | if (!fRes) 2477 | throw std::runtime_error("CKsEnumFilters: could not get interface alias"); 2478 | 2479 | // Check if the this interface alias is enabled. 2480 | if (!DIDAlias.Flags || (DIDAlias.Flags & SPINT_REMOVED)) 2481 | throw std::runtime_error("CKsEnumFilters: interface alias is not enabled"); 2482 | } 2483 | 2484 | std::auto_ptr pFilter(new TFilterType(pDevInterfaceDetails->DevicePath, friendlyName)); 2485 | 2486 | pFilter->Instantiate(); 2487 | pFilter->FindMidiPins(); 2488 | pFilter->Validate(); 2489 | 2490 | m_Filters.push_back(pFilter.release()); 2491 | } 2492 | catch (std::runtime_error const& e) { 2493 | } 2494 | } 2495 | 2496 | ::SetupDiDestroyDeviceInfoList(hDevInfo); 2497 | } 2498 | 2499 | private: 2500 | void DestroyLists() 2501 | { 2502 | for (size_t i=0;i < m_Filters.size();++i) 2503 | delete m_Filters[i]; 2504 | m_Filters.clear(); 2505 | } 2506 | 2507 | public: 2508 | // TODO: make this private. 2509 | std::vector m_Filters; 2510 | }; 2511 | 2512 | class CKsObject 2513 | { 2514 | public: 2515 | CKsObject(HANDLE handle) : m_handle(handle) 2516 | { 2517 | } 2518 | 2519 | protected: 2520 | HANDLE m_handle; 2521 | 2522 | void SetProperty(REFGUID guidPropertySet, ULONG nProperty, void* pvValue, ULONG cbValue) 2523 | { 2524 | KSPROPERTY ksProperty; 2525 | memset(&ksProperty, 0, sizeof ksProperty); 2526 | ksProperty.Set = guidPropertySet; 2527 | ksProperty.Id = nProperty; 2528 | ksProperty.Flags = KSPROPERTY_TYPE_SET; 2529 | 2530 | HRESULT hr = DeviceIoControlKsProperty(ksProperty, pvValue, cbValue); 2531 | if (FAILED(hr)) 2532 | throw ComException("CKsObject::SetProperty: could not set property", hr); 2533 | } 2534 | 2535 | private: 2536 | 2537 | HRESULT DeviceIoControlKsProperty(KSPROPERTY& ksProperty, void* pvValue, ULONG cbValue) 2538 | { 2539 | ULONG ulReturned; 2540 | return ::DeviceIoControl( 2541 | m_handle, 2542 | IOCTL_KS_PROPERTY, 2543 | &ksProperty, 2544 | sizeof(ksProperty), 2545 | pvValue, 2546 | cbValue, 2547 | &ulReturned, 2548 | NULL); 2549 | } 2550 | }; 2551 | 2552 | class CKsPin; 2553 | 2554 | class CKsFilter : public CKsObject 2555 | { 2556 | friend class CKsPin; 2557 | 2558 | public: 2559 | CKsFilter(tstring const& name, std::string const& sFriendlyName); 2560 | virtual ~CKsFilter(); 2561 | 2562 | virtual void Instantiate(); 2563 | 2564 | template 2565 | T GetPinProperty(ULONG nPinId, ULONG nProperty) 2566 | { 2567 | ULONG ulReturned = 0; 2568 | T value; 2569 | 2570 | KSP_PIN ksPProp; 2571 | ksPProp.Property.Set = KSPROPSETID_Pin; 2572 | ksPProp.Property.Id = nProperty; 2573 | ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; 2574 | ksPProp.PinId = nPinId; 2575 | ksPProp.Reserved = 0; 2576 | 2577 | HRESULT hr = ::DeviceIoControl( 2578 | m_handle, 2579 | IOCTL_KS_PROPERTY, 2580 | &ksPProp, 2581 | sizeof(KSP_PIN), 2582 | &value, 2583 | sizeof(value), 2584 | &ulReturned, 2585 | NULL); 2586 | if (FAILED(hr)) 2587 | throw ComException("CKsFilter::GetPinProperty: failed to retrieve property", hr); 2588 | 2589 | return value; 2590 | } 2591 | 2592 | void GetPinPropertyMulti(ULONG nPinId, REFGUID guidPropertySet, ULONG nProperty, PKSMULTIPLE_ITEM* ppKsMultipleItem) 2593 | { 2594 | HRESULT hr; 2595 | 2596 | KSP_PIN ksPProp; 2597 | ksPProp.Property.Set = guidPropertySet; 2598 | ksPProp.Property.Id = nProperty; 2599 | ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; 2600 | ksPProp.PinId = nPinId; 2601 | ksPProp.Reserved = 0; 2602 | 2603 | ULONG cbMultipleItem = 0; 2604 | hr = ::DeviceIoControl(m_handle, 2605 | IOCTL_KS_PROPERTY, 2606 | &ksPProp.Property, 2607 | sizeof(KSP_PIN), 2608 | NULL, 2609 | 0, 2610 | &cbMultipleItem, 2611 | NULL); 2612 | if (FAILED(hr)) 2613 | throw ComException("CKsFilter::GetPinPropertyMulti: cannot get property", hr); 2614 | 2615 | *ppKsMultipleItem = (PKSMULTIPLE_ITEM) new BYTE[cbMultipleItem]; 2616 | 2617 | ULONG ulReturned = 0; 2618 | hr = ::DeviceIoControl( 2619 | m_handle, 2620 | IOCTL_KS_PROPERTY, 2621 | &ksPProp, 2622 | sizeof(KSP_PIN), 2623 | (PVOID)*ppKsMultipleItem, 2624 | cbMultipleItem, 2625 | &ulReturned, 2626 | NULL); 2627 | if (FAILED(hr)) 2628 | throw ComException("CKsFilter::GetPinPropertyMulti: cannot get property", hr); 2629 | } 2630 | 2631 | std::string const& GetFriendlyName() const 2632 | { 2633 | return m_sFriendlyName; 2634 | } 2635 | 2636 | protected: 2637 | 2638 | std::vector m_Pins; // this list owns the pins. 2639 | 2640 | std::vector m_RenderPins; 2641 | std::vector m_CapturePins; 2642 | 2643 | private: 2644 | std::string const m_sFriendlyName; // friendly name eg "Virus TI Synth" 2645 | tstring const m_sName; // Filter path, eg "\\?\usb#vid_133e&pid_0815...\vtimidi02" 2646 | }; 2647 | 2648 | class CKsPin : public CKsObject 2649 | { 2650 | public: 2651 | CKsPin(CKsFilter* pFilter, ULONG nId); 2652 | virtual ~CKsPin(); 2653 | 2654 | virtual void Instantiate(); 2655 | 2656 | void ClosePin(); 2657 | 2658 | void SetState(KSSTATE ksState); 2659 | 2660 | void WriteData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED); 2661 | void ReadData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED); 2662 | 2663 | KSPIN_DATAFLOW GetDataFlow() const 2664 | { 2665 | return m_DataFlow; 2666 | } 2667 | 2668 | bool IsSink() const 2669 | { 2670 | return m_Communication == KSPIN_COMMUNICATION_SINK 2671 | || m_Communication == KSPIN_COMMUNICATION_BOTH; 2672 | } 2673 | 2674 | 2675 | protected: 2676 | PKSPIN_CONNECT m_pKsPinConnect; // creation parameters of pin 2677 | CKsFilter* const m_pFilter; 2678 | 2679 | ULONG m_cInterfaces; 2680 | PKSIDENTIFIER m_pInterfaces; 2681 | PKSMULTIPLE_ITEM m_pmiInterfaces; 2682 | 2683 | ULONG m_cMediums; 2684 | PKSIDENTIFIER m_pMediums; 2685 | PKSMULTIPLE_ITEM m_pmiMediums; 2686 | 2687 | ULONG m_cDataRanges; 2688 | PKSDATARANGE m_pDataRanges; 2689 | PKSMULTIPLE_ITEM m_pmiDataRanges; 2690 | 2691 | KSPIN_DATAFLOW m_DataFlow; 2692 | KSPIN_COMMUNICATION m_Communication; 2693 | }; 2694 | 2695 | CKsFilter::CKsFilter(tstring const& sName, std::string const& sFriendlyName) : 2696 | CKsObject(INVALID_HANDLE_VALUE), 2697 | m_sFriendlyName(sFriendlyName), 2698 | m_sName(sName) 2699 | { 2700 | if (sName.empty()) 2701 | throw std::runtime_error("CKsFilter::CKsFilter: name can't be empty"); 2702 | } 2703 | 2704 | CKsFilter::~CKsFilter() 2705 | { 2706 | for (size_t i=0;i < m_Pins.size();++i) 2707 | delete m_Pins[i]; 2708 | 2709 | if (IsValid(m_handle)) 2710 | ::CloseHandle(m_handle); 2711 | } 2712 | 2713 | void CKsFilter::Instantiate() 2714 | { 2715 | m_handle = CreateFile( 2716 | m_sName.c_str(), 2717 | GENERIC_READ | GENERIC_WRITE, 2718 | 0, 2719 | NULL, 2720 | OPEN_EXISTING, 2721 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 2722 | NULL); 2723 | 2724 | if (!IsValid(m_handle)) 2725 | { 2726 | DWORD const dwError = GetLastError(); 2727 | throw ComException("CKsFilter::Instantiate: can't open driver", HRESULT_FROM_WIN32(dwError)); 2728 | } 2729 | } 2730 | 2731 | CKsPin::CKsPin(CKsFilter* pFilter, ULONG PinId) : 2732 | CKsObject(INVALID_HANDLE_VALUE), 2733 | m_pKsPinConnect(NULL), 2734 | m_pFilter(pFilter) 2735 | { 2736 | m_Communication = m_pFilter->GetPinProperty(PinId, KSPROPERTY_PIN_COMMUNICATION); 2737 | m_DataFlow = m_pFilter->GetPinProperty(PinId, KSPROPERTY_PIN_DATAFLOW); 2738 | 2739 | // Interfaces 2740 | m_pFilter->GetPinPropertyMulti( 2741 | PinId, 2742 | KSPROPSETID_Pin, 2743 | KSPROPERTY_PIN_INTERFACES, 2744 | &m_pmiInterfaces); 2745 | 2746 | m_cInterfaces = m_pmiInterfaces->Count; 2747 | m_pInterfaces = (PKSPIN_INTERFACE)(m_pmiInterfaces + 1); 2748 | 2749 | // Mediums 2750 | m_pFilter->GetPinPropertyMulti( 2751 | PinId, 2752 | KSPROPSETID_Pin, 2753 | KSPROPERTY_PIN_MEDIUMS, 2754 | &m_pmiMediums); 2755 | 2756 | m_cMediums = m_pmiMediums->Count; 2757 | m_pMediums = (PKSPIN_MEDIUM)(m_pmiMediums + 1); 2758 | 2759 | // Data ranges 2760 | m_pFilter->GetPinPropertyMulti( 2761 | PinId, 2762 | KSPROPSETID_Pin, 2763 | KSPROPERTY_PIN_DATARANGES, 2764 | &m_pmiDataRanges); 2765 | 2766 | m_cDataRanges = m_pmiDataRanges->Count; 2767 | m_pDataRanges = (PKSDATARANGE)(m_pmiDataRanges + 1); 2768 | } 2769 | 2770 | CKsPin::~CKsPin() 2771 | { 2772 | ClosePin(); 2773 | 2774 | delete[] (BYTE*)m_pKsPinConnect; 2775 | delete[] (BYTE*)m_pmiDataRanges; 2776 | delete[] (BYTE*)m_pmiInterfaces; 2777 | delete[] (BYTE*)m_pmiMediums; 2778 | } 2779 | 2780 | void CKsPin::ClosePin() 2781 | { 2782 | if (IsValid(m_handle)) { 2783 | SetState(KSSTATE_STOP); 2784 | ::CloseHandle(m_handle); 2785 | } 2786 | m_handle = INVALID_HANDLE_VALUE; 2787 | } 2788 | 2789 | void CKsPin::SetState(KSSTATE ksState) 2790 | { 2791 | SetProperty(KSPROPSETID_Connection, KSPROPERTY_CONNECTION_STATE, &ksState, sizeof(ksState)); 2792 | } 2793 | 2794 | void CKsPin::Instantiate() 2795 | { 2796 | if (!m_pKsPinConnect) 2797 | throw std::runtime_error("CKsPin::Instanciate: abstract pin"); 2798 | 2799 | DWORD const dwResult = KsCreatePin(m_pFilter->m_handle, m_pKsPinConnect, GENERIC_WRITE | GENERIC_READ, &m_handle); 2800 | if (dwResult != ERROR_SUCCESS) 2801 | throw ComException("CKsMidiCapFilter::CreateRenderPin: Pin instanciation failed", HRESULT_FROM_WIN32(dwResult)); 2802 | } 2803 | 2804 | void CKsPin::WriteData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED) 2805 | { 2806 | DWORD cbWritten; 2807 | BOOL fRes = ::DeviceIoControl( 2808 | m_handle, 2809 | IOCTL_KS_WRITE_STREAM, 2810 | NULL, 2811 | 0, 2812 | pKSSTREAM_HEADER, 2813 | pKSSTREAM_HEADER->Size, 2814 | &cbWritten, 2815 | pOVERLAPPED); 2816 | if (!fRes) { 2817 | DWORD const dwError = GetLastError(); 2818 | if (dwError != ERROR_IO_PENDING) 2819 | throw ComException("CKsPin::WriteData: DeviceIoControl failed", HRESULT_FROM_WIN32(dwError)); 2820 | } 2821 | } 2822 | 2823 | void CKsPin::ReadData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED) 2824 | { 2825 | DWORD cbReturned; 2826 | BOOL fRes = ::DeviceIoControl( 2827 | m_handle, 2828 | IOCTL_KS_READ_STREAM, 2829 | NULL, 2830 | 0, 2831 | pKSSTREAM_HEADER, 2832 | pKSSTREAM_HEADER->Size, 2833 | &cbReturned, 2834 | pOVERLAPPED); 2835 | if (!fRes) { 2836 | DWORD const dwError = GetLastError(); 2837 | if (dwError != ERROR_IO_PENDING) 2838 | throw ComException("CKsPin::ReadData: DeviceIoControl failed", HRESULT_FROM_WIN32(dwError)); 2839 | } 2840 | } 2841 | 2842 | class CKsMidiFilter : public CKsFilter 2843 | { 2844 | public: 2845 | void FindMidiPins(); 2846 | 2847 | protected: 2848 | CKsMidiFilter(tstring const& sPath, std::string const& sFriendlyName); 2849 | }; 2850 | 2851 | class CKsMidiPin : public CKsPin 2852 | { 2853 | public: 2854 | CKsMidiPin(CKsFilter* pFilter, ULONG nId); 2855 | }; 2856 | 2857 | class CKsMidiRenFilter : public CKsMidiFilter 2858 | { 2859 | public: 2860 | CKsMidiRenFilter(tstring const& sPath, std::string const& sFriendlyName); 2861 | CKsMidiPin* CreateRenderPin(); 2862 | 2863 | void Validate() 2864 | { 2865 | if (m_RenderPins.empty()) 2866 | throw std::runtime_error("Could not find a MIDI render pin"); 2867 | } 2868 | }; 2869 | 2870 | class CKsMidiCapFilter : public CKsMidiFilter 2871 | { 2872 | public: 2873 | CKsMidiCapFilter(tstring const& sPath, std::string const& sFriendlyName); 2874 | CKsMidiPin* CreateCapturePin(); 2875 | 2876 | void Validate() 2877 | { 2878 | if (m_CapturePins.empty()) 2879 | throw std::runtime_error("Could not find a MIDI capture pin"); 2880 | } 2881 | }; 2882 | 2883 | CKsMidiFilter::CKsMidiFilter(tstring const& sPath, std::string const& sFriendlyName) : 2884 | CKsFilter(sPath, sFriendlyName) 2885 | { 2886 | } 2887 | 2888 | void CKsMidiFilter::FindMidiPins() 2889 | { 2890 | ULONG numPins = GetPinProperty(0, KSPROPERTY_PIN_CTYPES); 2891 | 2892 | for (ULONG iPin = 0; iPin < numPins; ++iPin) { 2893 | try { 2894 | KSPIN_COMMUNICATION com = GetPinProperty(iPin, KSPROPERTY_PIN_COMMUNICATION); 2895 | if (com != KSPIN_COMMUNICATION_SINK && com != KSPIN_COMMUNICATION_BOTH) 2896 | throw std::runtime_error("Unknown pin communication value"); 2897 | 2898 | m_Pins.push_back(new CKsMidiPin(this, iPin)); 2899 | } 2900 | catch (std::runtime_error const&) { 2901 | // pin instanciation has failed, continue to the next pin. 2902 | } 2903 | } 2904 | 2905 | m_RenderPins.clear(); 2906 | m_CapturePins.clear(); 2907 | 2908 | for (size_t i = 0; i < m_Pins.size(); ++i) { 2909 | CKsPin* const pPin = m_Pins[i]; 2910 | 2911 | if (pPin->IsSink()) { 2912 | if (pPin->GetDataFlow() == KSPIN_DATAFLOW_IN) 2913 | m_RenderPins.push_back(pPin); 2914 | else 2915 | m_CapturePins.push_back(pPin); 2916 | } 2917 | } 2918 | 2919 | if (m_RenderPins.empty() && m_CapturePins.empty()) 2920 | throw std::runtime_error("No valid pins found on the filter."); 2921 | } 2922 | 2923 | CKsMidiRenFilter::CKsMidiRenFilter(tstring const& sPath, std::string const& sFriendlyName) : 2924 | CKsMidiFilter(sPath, sFriendlyName) 2925 | { 2926 | } 2927 | 2928 | CKsMidiPin* CKsMidiRenFilter::CreateRenderPin() 2929 | { 2930 | if (m_RenderPins.empty()) 2931 | throw std::runtime_error("Could not find a MIDI render pin"); 2932 | 2933 | CKsMidiPin* pPin = (CKsMidiPin*)m_RenderPins[0]; 2934 | pPin->Instantiate(); 2935 | return pPin; 2936 | } 2937 | 2938 | CKsMidiCapFilter::CKsMidiCapFilter(tstring const& sPath, std::string const& sFriendlyName) : 2939 | CKsMidiFilter(sPath, sFriendlyName) 2940 | { 2941 | } 2942 | 2943 | CKsMidiPin* CKsMidiCapFilter::CreateCapturePin() 2944 | { 2945 | if (m_CapturePins.empty()) 2946 | throw std::runtime_error("Could not find a MIDI capture pin"); 2947 | 2948 | CKsMidiPin* pPin = (CKsMidiPin*)m_CapturePins[0]; 2949 | pPin->Instantiate(); 2950 | return pPin; 2951 | } 2952 | 2953 | CKsMidiPin::CKsMidiPin(CKsFilter* pFilter, ULONG nId) : 2954 | CKsPin(pFilter, nId) 2955 | { 2956 | DWORD const cbPinCreateSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT); 2957 | m_pKsPinConnect = (PKSPIN_CONNECT) new BYTE[cbPinCreateSize]; 2958 | 2959 | m_pKsPinConnect->Interface.Set = KSINTERFACESETID_Standard; 2960 | m_pKsPinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; 2961 | m_pKsPinConnect->Interface.Flags = 0; 2962 | m_pKsPinConnect->Medium.Set = KSMEDIUMSETID_Standard; 2963 | m_pKsPinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE; 2964 | m_pKsPinConnect->Medium.Flags = 0; 2965 | m_pKsPinConnect->PinId = nId; 2966 | m_pKsPinConnect->PinToHandle = NULL; 2967 | m_pKsPinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL; 2968 | m_pKsPinConnect->Priority.PrioritySubClass = 1; 2969 | 2970 | // point m_pDataFormat to just after the pConnect struct 2971 | KSDATAFORMAT* m_pDataFormat = (KSDATAFORMAT*)(m_pKsPinConnect + 1); 2972 | m_pDataFormat->FormatSize = sizeof(KSDATAFORMAT); 2973 | m_pDataFormat->Flags = 0; 2974 | m_pDataFormat->SampleSize = 0; 2975 | m_pDataFormat->Reserved = 0; 2976 | m_pDataFormat->MajorFormat = GUID(KSDATAFORMAT_TYPE_MUSIC); 2977 | m_pDataFormat->SubFormat = GUID(KSDATAFORMAT_SUBTYPE_MIDI); 2978 | m_pDataFormat->Specifier = GUID(KSDATAFORMAT_SPECIFIER_NONE); 2979 | 2980 | bool hasStdStreamingInterface = false; 2981 | bool hasStdStreamingMedium = false; 2982 | 2983 | for ( ULONG i = 0; i < m_cInterfaces; i++ ) { 2984 | if (m_pInterfaces[i].Set == KSINTERFACESETID_Standard 2985 | && m_pInterfaces[i].Id == KSINTERFACE_STANDARD_STREAMING) 2986 | hasStdStreamingInterface = true; 2987 | } 2988 | 2989 | for (ULONG i = 0; i < m_cMediums; i++) { 2990 | if (m_pMediums[i].Set == KSMEDIUMSETID_Standard 2991 | && m_pMediums[i].Id == KSMEDIUM_STANDARD_DEVIO) 2992 | hasStdStreamingMedium = true; 2993 | } 2994 | 2995 | if (!hasStdStreamingInterface) // No standard streaming interfaces on the pin 2996 | throw std::runtime_error("CKsMidiPin::CKsMidiPin: no standard streaming interface"); 2997 | 2998 | if (!hasStdStreamingMedium) // No standard streaming mediums on the pin 2999 | throw std::runtime_error("CKsMidiPin::CKsMidiPin: no standard streaming medium"); 3000 | 3001 | bool hasMidiDataRange = false; 3002 | 3003 | BYTE const* pDataRangePtr = reinterpret_cast(m_pDataRanges); 3004 | 3005 | for (ULONG i = 0; i < m_cDataRanges; ++i) { 3006 | KSDATARANGE const* pDataRange = reinterpret_cast(pDataRangePtr); 3007 | 3008 | if (pDataRange->SubFormat == KSDATAFORMAT_SUBTYPE_MIDI) { 3009 | hasMidiDataRange = true; 3010 | break; 3011 | } 3012 | 3013 | pDataRangePtr += pDataRange->FormatSize; 3014 | } 3015 | 3016 | if (!hasMidiDataRange) // No MIDI dataranges on the pin 3017 | throw std::runtime_error("CKsMidiPin::CKsMidiPin: no MIDI datarange"); 3018 | } 3019 | 3020 | 3021 | struct WindowsKsData 3022 | { 3023 | WindowsKsData() : m_pPin(NULL), m_Buffer(1024), m_hInputThread(NULL) 3024 | { 3025 | memset(&overlapped, 0, sizeof(OVERLAPPED)); 3026 | m_hExitEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); 3027 | overlapped.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); 3028 | m_hInputThread = NULL; 3029 | } 3030 | 3031 | ~WindowsKsData() 3032 | { 3033 | ::CloseHandle(overlapped.hEvent); 3034 | ::CloseHandle(m_hExitEvent); 3035 | } 3036 | 3037 | OVERLAPPED overlapped; 3038 | CKsPin* m_pPin; 3039 | std::vector m_Buffer; 3040 | std::auto_ptr > m_pCaptureEnum; 3041 | std::auto_ptr > m_pRenderEnum; 3042 | HANDLE m_hInputThread; 3043 | HANDLE m_hExitEvent; 3044 | }; 3045 | 3046 | // *********************************************************************// 3047 | // API: WINDOWS Kernel Streaming 3048 | // Class Definitions: MidiInWinKS 3049 | // *********************************************************************// 3050 | 3051 | DWORD WINAPI midiKsInputThread(VOID* pUser) 3052 | { 3053 | MidiInApi::RtMidiInData* data = static_cast(pUser); 3054 | WindowsKsData* apiData = static_cast(data->apiData); 3055 | 3056 | HANDLE hEvents[] = { apiData->overlapped.hEvent, apiData->m_hExitEvent }; 3057 | 3058 | while ( true ) { 3059 | KSSTREAM_HEADER packet; 3060 | memset(&packet, 0, sizeof packet); 3061 | packet.Size = sizeof(KSSTREAM_HEADER); 3062 | packet.PresentationTime.Time = 0; 3063 | packet.PresentationTime.Numerator = 1; 3064 | packet.PresentationTime.Denominator = 1; 3065 | packet.Data = &apiData->m_Buffer[0]; 3066 | packet.DataUsed = 0; 3067 | packet.FrameExtent = apiData->m_Buffer.size(); 3068 | apiData->m_pPin->ReadData(&packet, &apiData->overlapped); 3069 | 3070 | DWORD dwRet = ::WaitForMultipleObjects(2, hEvents, FALSE, INFINITE); 3071 | 3072 | if ( dwRet == WAIT_OBJECT_0 ) { 3073 | // parse packet 3074 | unsigned char* pData = (unsigned char*)packet.Data; 3075 | unsigned int iOffset = 0; 3076 | 3077 | while ( iOffset < packet.DataUsed ) { 3078 | KSMUSICFORMAT* pMusic = (KSMUSICFORMAT*)&pData[iOffset]; 3079 | iOffset += sizeof(KSMUSICFORMAT); 3080 | 3081 | MidiInApi::MidiMessage message; 3082 | message.timeStamp = 0; 3083 | for(size_t i=0;i < pMusic->ByteCount;++i) 3084 | message.bytes.push_back(pData[iOffset+i]); 3085 | 3086 | if ( data->usingCallback ) { 3087 | RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback)data->userCallback; 3088 | callback(message.timeStamp, &message.bytes, data->userData); 3089 | } 3090 | else { 3091 | // As long as we haven't reached our queue size limit, push the message. 3092 | if ( data->queue.size < data->queue.ringSize ) { 3093 | data->queue.ring[data->queue.back++] = message; 3094 | if(data->queue.back == data->queue.ringSize) 3095 | data->queue.back = 0; 3096 | data->queue.size++; 3097 | } 3098 | else 3099 | std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; 3100 | } 3101 | 3102 | iOffset += pMusic->ByteCount; 3103 | 3104 | // re-align on 32 bits 3105 | if ( iOffset % 4 != 0 ) 3106 | iOffset += (4 - iOffset % 4); 3107 | } 3108 | } 3109 | else 3110 | break; 3111 | } 3112 | return 0; 3113 | } 3114 | 3115 | MidiInWinKS :: MidiInWinKS( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) 3116 | { 3117 | initialize( clientName ); 3118 | } 3119 | 3120 | void MidiInWinKS :: initialize( const std::string& clientName ) 3121 | { 3122 | WindowsKsData* data = new WindowsKsData; 3123 | apiData_ = (void*)data; 3124 | inputData_.apiData = data; 3125 | 3126 | GUID const aguidEnumCats[] = 3127 | { 3128 | { STATIC_KSCATEGORY_AUDIO }, { STATIC_KSCATEGORY_CAPTURE } 3129 | }; 3130 | data->m_pCaptureEnum.reset(new CKsEnumFilters ); 3131 | data->m_pCaptureEnum->EnumFilters(aguidEnumCats, 2); 3132 | } 3133 | 3134 | MidiInWinKS :: ~MidiInWinKS() 3135 | { 3136 | WindowsKsData* data = static_cast(apiData_); 3137 | try { 3138 | if ( data->m_pPin ) 3139 | closePort(); 3140 | } 3141 | catch(...) { 3142 | } 3143 | 3144 | delete data; 3145 | } 3146 | 3147 | void MidiInWinKS :: openPort( unsigned int portNumber, const std::string portName ) 3148 | { 3149 | WindowsKsData* data = static_cast(apiData_); 3150 | 3151 | if ( portNumber < 0 || portNumber >= data->m_pCaptureEnum->m_Filters.size() ) { 3152 | std::stringstream ost; 3153 | ost << "MidiInWinKS::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 3154 | errorString_ = ost.str(); 3155 | RtMidi::error( RtError::WARNING, errorString_ ); 3156 | } 3157 | 3158 | CKsMidiCapFilter* pFilter = data->m_pCaptureEnum->m_Filters[portNumber]; 3159 | data->m_pPin = pFilter->CreateCapturePin(); 3160 | 3161 | if ( data->m_pPin == NULL ) { 3162 | std::stringstream ost; 3163 | ost << "MidiInWinKS::openPort: KS error opening port (could not create pin)"; 3164 | errorString_ = ost.str(); 3165 | RtMidi::error( RtError::WARNING, errorString_ ); 3166 | } 3167 | 3168 | data->m_pPin->SetState(KSSTATE_RUN); 3169 | 3170 | DWORD threadId; 3171 | data->m_hInputThread = ::CreateThread(NULL, 0, &midiKsInputThread, &inputData_, 0, &threadId); 3172 | if ( data->m_hInputThread == NULL ) { 3173 | std::stringstream ost; 3174 | ost << "MidiInWinKS::initialize: Could not create input thread : Windows error " << GetLastError() << std::endl;; 3175 | errorString_ = ost.str(); 3176 | RtMidi::error( RtError::WARNING, errorString_ ); 3177 | } 3178 | 3179 | connected_ = true; 3180 | } 3181 | 3182 | void MidiInWinKS :: openVirtualPort( const std::string portName ) 3183 | { 3184 | // This function cannot be implemented for the Windows KS MIDI API. 3185 | errorString_ = "MidiInWinKS::openVirtualPort: cannot be implemented in Windows KS MIDI API!"; 3186 | RtMidi::error( RtError::WARNING, errorString_ ); 3187 | } 3188 | 3189 | unsigned int MidiInWinKS :: getPortCount() 3190 | { 3191 | WindowsKsData* data = static_cast(apiData_); 3192 | return (unsigned int)data->m_pCaptureEnum->m_Filters.size(); 3193 | } 3194 | 3195 | std::string MidiInWinKS :: getPortName(unsigned int portNumber) 3196 | { 3197 | WindowsKsData* data = static_cast(apiData_); 3198 | 3199 | if(portNumber < 0 || portNumber >= data->m_pCaptureEnum->m_Filters.size()) { 3200 | std::stringstream ost; 3201 | ost << "MidiInWinKS::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 3202 | errorString_ = ost.str(); 3203 | RtMidi::error( RtError::WARNING, errorString_ ); 3204 | } 3205 | 3206 | CKsMidiCapFilter* pFilter = data->m_pCaptureEnum->m_Filters[portNumber]; 3207 | return pFilter->GetFriendlyName(); 3208 | } 3209 | 3210 | void MidiInWinKS :: closePort() 3211 | { 3212 | WindowsKsData* data = static_cast(apiData_); 3213 | connected_ = false; 3214 | 3215 | if(data->m_hInputThread) { 3216 | ::SignalObjectAndWait(data->m_hExitEvent, data->m_hInputThread, INFINITE, FALSE); 3217 | ::CloseHandle(data->m_hInputThread); 3218 | } 3219 | 3220 | if(data->m_pPin) { 3221 | data->m_pPin->SetState(KSSTATE_PAUSE); 3222 | data->m_pPin->SetState(KSSTATE_STOP); 3223 | data->m_pPin->ClosePin(); 3224 | data->m_pPin = NULL; 3225 | } 3226 | } 3227 | 3228 | // *********************************************************************// 3229 | // API: WINDOWS Kernel Streaming 3230 | // Class Definitions: MidiOutWinKS 3231 | // *********************************************************************// 3232 | 3233 | MidiOutWinKS :: MidiOutWinKS( const std::string clientName ) : MidiOutApi() 3234 | { 3235 | initialize( clientName ); 3236 | } 3237 | 3238 | void MidiOutWinKS :: initialize( const std::string& clientName ) 3239 | { 3240 | WindowsKsData* data = new WindowsKsData; 3241 | 3242 | data->m_pPin = NULL; 3243 | data->m_pRenderEnum.reset(new CKsEnumFilters ); 3244 | GUID const aguidEnumCats[] = 3245 | { 3246 | { STATIC_KSCATEGORY_AUDIO }, { STATIC_KSCATEGORY_RENDER } 3247 | }; 3248 | data->m_pRenderEnum->EnumFilters(aguidEnumCats, 2); 3249 | 3250 | apiData_ = (void*)data; 3251 | } 3252 | 3253 | MidiOutWinKS :: ~MidiOutWinKS() 3254 | { 3255 | // Close a connection if it exists. 3256 | closePort(); 3257 | 3258 | // Cleanup. 3259 | WindowsKsData* data = static_cast(apiData_); 3260 | delete data; 3261 | } 3262 | 3263 | void MidiOutWinKS :: openPort( unsigned int portNumber, const std::string portName ) 3264 | { 3265 | WindowsKsData* data = static_cast(apiData_); 3266 | 3267 | if(portNumber < 0 || portNumber >= data->m_pRenderEnum->m_Filters.size()) { 3268 | std::stringstream ost; 3269 | ost << "MidiOutWinKS::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; 3270 | errorString_ = ost.str(); 3271 | RtMidi::error( RtError::WARNING, errorString_ ); 3272 | } 3273 | 3274 | CKsMidiRenFilter* pFilter = data->m_pRenderEnum->m_Filters[portNumber]; 3275 | data->m_pPin = pFilter->CreateRenderPin(); 3276 | 3277 | if(data->m_pPin == NULL) { 3278 | std::stringstream ost; 3279 | ost << "MidiOutWinKS::openPort: KS error opening port (could not create pin)"; 3280 | errorString_ = ost.str(); 3281 | RtMidi::error( RtError::WARNING, errorString_ ); 3282 | } 3283 | 3284 | data->m_pPin->SetState(KSSTATE_RUN); 3285 | connected_ = true; 3286 | } 3287 | 3288 | void MidiOutWinKS :: openVirtualPort( const std::string portName ) 3289 | { 3290 | // This function cannot be implemented for the Windows KS MIDI API. 3291 | errorString_ = "MidiOutWinKS::openVirtualPort: cannot be implemented in Windows KS MIDI API!"; 3292 | RtMidi::error( RtError::WARNING, errorString_ ); 3293 | } 3294 | 3295 | unsigned int MidiOutWinKS :: getPortCount() 3296 | { 3297 | WindowsKsData* data = static_cast(apiData_); 3298 | 3299 | return (unsigned int)data->m_pRenderEnum->m_Filters.size(); 3300 | } 3301 | 3302 | std::string MidiOutWinKS :: getPortName( unsigned int portNumber ) 3303 | { 3304 | WindowsKsData* data = static_cast(apiData_); 3305 | 3306 | if ( portNumber < 0 || portNumber >= data->m_pRenderEnum->m_Filters.size() ) { 3307 | std::stringstream ost; 3308 | ost << "MidiOutWinKS::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 3309 | errorString_ = ost.str(); 3310 | RtMidi::error( RtError::WARNING, errorString_ ); 3311 | } 3312 | 3313 | CKsMidiRenFilter* pFilter = data->m_pRenderEnum->m_Filters[portNumber]; 3314 | return pFilter->GetFriendlyName(); 3315 | } 3316 | 3317 | void MidiOutWinKS :: closePort() 3318 | { 3319 | WindowsKsData* data = static_cast(apiData_); 3320 | connected_ = false; 3321 | 3322 | if ( data->m_pPin ) { 3323 | data->m_pPin->SetState(KSSTATE_PAUSE); 3324 | data->m_pPin->SetState(KSSTATE_STOP); 3325 | data->m_pPin->ClosePin(); 3326 | data->m_pPin = NULL; 3327 | } 3328 | } 3329 | 3330 | void MidiOutWinKS :: sendMessage(std::vector* pMessage) 3331 | { 3332 | std::vector const& msg = *pMessage; 3333 | WindowsKsData* data = static_cast(apiData_); 3334 | size_t iNumMidiBytes = msg.size(); 3335 | size_t pos = 0; 3336 | 3337 | // write header 3338 | KSMUSICFORMAT* pKsMusicFormat = reinterpret_cast(&data->m_Buffer[pos]); 3339 | pKsMusicFormat->TimeDeltaMs = 0; 3340 | pKsMusicFormat->ByteCount = iNumMidiBytes; 3341 | pos += sizeof(KSMUSICFORMAT); 3342 | 3343 | // write MIDI bytes 3344 | if ( pos + iNumMidiBytes > data->m_Buffer.size() ) { 3345 | std::stringstream ost; 3346 | ost << "KsMidiInput::Write: MIDI buffer too small. Required " << pos + iNumMidiBytes << " bytes, only has " << data->m_Buffer.size(); 3347 | errorString_ = ost.str(); 3348 | RtMidi::error( RtError::WARNING, errorString_ ); 3349 | } 3350 | 3351 | if ( data->m_pPin == NULL ) { 3352 | std::stringstream ost; 3353 | ost << "MidiOutWinKS::sendMessage: port is not open"; 3354 | errorString_ = ost.str(); 3355 | RtMidi::error( RtError::WARNING, errorString_ ); 3356 | } 3357 | 3358 | memcpy(&data->m_Buffer[pos], &msg[0], iNumMidiBytes); 3359 | pos += iNumMidiBytes; 3360 | 3361 | KSSTREAM_HEADER packet; 3362 | memset(&packet, 0, sizeof packet); 3363 | packet.Size = sizeof(packet); 3364 | packet.PresentationTime.Time = 0; 3365 | packet.PresentationTime.Numerator = 1; 3366 | packet.PresentationTime.Denominator = 1; 3367 | packet.Data = const_cast(&data->m_Buffer[0]); 3368 | packet.DataUsed = ((pos+3)/4)*4; 3369 | packet.FrameExtent = data->m_Buffer.size(); 3370 | 3371 | data->m_pPin->WriteData(&packet, NULL); 3372 | } 3373 | 3374 | #endif // __WINDOWS_KS__ 3375 | 3376 | //*********************************************************************// 3377 | // API: UNIX JACK 3378 | // 3379 | // Written primarily by Alexander Svetalkin, with updates for delta 3380 | // time by Gary Scavone, April 2011. 3381 | // 3382 | // *********************************************************************// 3383 | 3384 | #if defined(__UNIX_JACK__) 3385 | 3386 | // JACK header files 3387 | #include 3388 | #include 3389 | #include 3390 | 3391 | #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer 3392 | 3393 | struct JackMidiData { 3394 | jack_client_t *client; 3395 | jack_port_t *port; 3396 | jack_ringbuffer_t *buffSize; 3397 | jack_ringbuffer_t *buffMessage; 3398 | jack_time_t lastTime; 3399 | MidiInApi :: RtMidiInData *rtMidiIn; 3400 | }; 3401 | 3402 | //*********************************************************************// 3403 | // API: JACK 3404 | // Class Definitions: MidiInJack 3405 | //*********************************************************************// 3406 | 3407 | int jackProcessIn( jack_nframes_t nframes, void *arg ) 3408 | { 3409 | JackMidiData *jData = (JackMidiData *) arg; 3410 | MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn; 3411 | jack_midi_event_t event; 3412 | jack_time_t long long time; 3413 | 3414 | // Is port created? 3415 | if ( jData->port == NULL ) return 0; 3416 | void *buff = jack_port_get_buffer( jData->port, nframes ); 3417 | 3418 | // We have midi events in buffer 3419 | int evCount = jack_midi_get_event_count( buff ); 3420 | if ( evCount > 0 ) { 3421 | MidiInApi::MidiMessage message; 3422 | message.bytes.clear(); 3423 | 3424 | jack_midi_event_get( &event, buff, 0 ); 3425 | 3426 | for (unsigned int i = 0; i < event.size; i++ ) 3427 | message.bytes.push_back( event.buffer[i] ); 3428 | 3429 | // Compute the delta time. 3430 | time = jack_get_time(); 3431 | if ( rtData->firstMessage == true ) 3432 | rtData->firstMessage = false; 3433 | else 3434 | message.timeStamp = ( time - jData->lastTime ) * 0.000001; 3435 | 3436 | jData->lastTime = time; 3437 | 3438 | if ( !rtData->continueSysex ) { 3439 | if ( rtData->usingCallback ) { 3440 | RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback; 3441 | callback( message.timeStamp, &message.bytes, rtData->userData ); 3442 | } 3443 | else { 3444 | // As long as we haven't reached our queue size limit, push the message. 3445 | if ( rtData->queue.size < rtData->queue.ringSize ) { 3446 | rtData->queue.ring[rtData->queue.back++] = message; 3447 | if ( rtData->queue.back == rtData->queue.ringSize ) 3448 | rtData->queue.back = 0; 3449 | rtData->queue.size++; 3450 | } 3451 | else 3452 | std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; 3453 | } 3454 | } 3455 | } 3456 | 3457 | return 0; 3458 | } 3459 | 3460 | MidiInJack :: MidiInJack( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) 3461 | { 3462 | initialize( clientName ); 3463 | } 3464 | 3465 | void MidiInJack :: initialize( const std::string& clientName ) 3466 | { 3467 | JackMidiData *data = new JackMidiData; 3468 | apiData_ = (void *) data; 3469 | 3470 | // Initialize JACK client 3471 | if (( data->client = jack_client_open( clientName.c_str(), JackNullOption, NULL )) == 0) { 3472 | errorString_ = "MidiInJack::initialize: JACK server not running?"; 3473 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 3474 | return; 3475 | } 3476 | 3477 | data->rtMidiIn = &inputData_; 3478 | data->port = NULL; 3479 | 3480 | jack_set_process_callback( data->client, jackProcessIn, data ); 3481 | jack_activate( data->client ); 3482 | } 3483 | 3484 | MidiInJack :: ~MidiInJack() 3485 | { 3486 | JackMidiData *data = static_cast (apiData_); 3487 | closePort(); 3488 | 3489 | jack_client_close( data->client ); 3490 | } 3491 | 3492 | void MidiInJack :: openPort( unsigned int portNumber, const std::string portName ) 3493 | { 3494 | JackMidiData *data = static_cast (apiData_); 3495 | 3496 | // Creating new port 3497 | if ( data->port == NULL) 3498 | data->port = jack_port_register( data->client, portName.c_str(), 3499 | JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); 3500 | 3501 | if ( data->port == NULL) { 3502 | errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; 3503 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 3504 | } 3505 | 3506 | // Connecting to the output 3507 | std::string name = getPortName( portNumber ); 3508 | jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); 3509 | } 3510 | 3511 | void MidiInJack :: openVirtualPort( const std::string portName ) 3512 | { 3513 | JackMidiData *data = static_cast (apiData_); 3514 | 3515 | if ( data->port == NULL ) 3516 | data->port = jack_port_register( data->client, portName.c_str(), 3517 | JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); 3518 | 3519 | if ( data->port == NULL ) { 3520 | errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; 3521 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 3522 | } 3523 | } 3524 | 3525 | unsigned int MidiInJack :: getPortCount() 3526 | { 3527 | int count = 0; 3528 | JackMidiData *data = static_cast (apiData_); 3529 | 3530 | // List of available ports 3531 | const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); 3532 | 3533 | if ( ports == NULL ) return 0; 3534 | while ( ports[count] != NULL ) 3535 | count++; 3536 | 3537 | free( ports ); 3538 | 3539 | return count; 3540 | } 3541 | 3542 | std::string MidiInJack :: getPortName( unsigned int portNumber ) 3543 | { 3544 | JackMidiData *data = static_cast (apiData_); 3545 | std::ostringstream ost; 3546 | std::string retStr(""); 3547 | 3548 | // List of available ports 3549 | const char **ports = jack_get_ports( data->client, NULL, 3550 | JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); 3551 | 3552 | // Check port validity 3553 | if ( ports == NULL ) { 3554 | errorString_ = "MidiInJack::getPortName: no ports available!"; 3555 | RtMidi::error( RtError::WARNING, errorString_ ); 3556 | return retStr; 3557 | } 3558 | 3559 | if ( ports[portNumber] == NULL ) { 3560 | ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 3561 | errorString_ = ost.str(); 3562 | RtMidi::error( RtError::WARNING, errorString_ ); 3563 | } 3564 | else retStr.assign( ports[portNumber] ); 3565 | 3566 | free( ports ); 3567 | 3568 | return retStr; 3569 | } 3570 | 3571 | void MidiInJack :: closePort() 3572 | { 3573 | JackMidiData *data = static_cast (apiData_); 3574 | 3575 | if ( data->port == NULL ) return; 3576 | jack_port_unregister( data->client, data->port ); 3577 | data->port = NULL; 3578 | } 3579 | 3580 | //*********************************************************************// 3581 | // API: JACK 3582 | // Class Definitions: MidiOutJack 3583 | //*********************************************************************// 3584 | 3585 | // Jack process callback 3586 | int jackProcessOut( jack_nframes_t nframes, void *arg ) 3587 | { 3588 | JackMidiData *data = (JackMidiData *) arg; 3589 | jack_midi_data_t *midiData; 3590 | int space; 3591 | 3592 | // Is port created? 3593 | if ( data->port == NULL ) return 0; 3594 | 3595 | void *buff = jack_port_get_buffer( data->port, nframes ); 3596 | jack_midi_clear_buffer( buff ); 3597 | 3598 | while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) { 3599 | jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) ); 3600 | midiData = jack_midi_event_reserve( buff, 0, space ); 3601 | 3602 | jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); 3603 | } 3604 | 3605 | return 0; 3606 | } 3607 | 3608 | MidiOutJack :: MidiOutJack( const std::string clientName ) : MidiOutApi() 3609 | { 3610 | initialize( clientName ); 3611 | } 3612 | 3613 | void MidiOutJack :: initialize( const std::string& clientName ) 3614 | { 3615 | JackMidiData *data = new JackMidiData; 3616 | 3617 | data->port = NULL; 3618 | 3619 | // Initialize JACK client 3620 | if (( data->client = jack_client_open( clientName.c_str(), JackNullOption, NULL )) == 0) { 3621 | errorString_ = "MidiOutJack::initialize: JACK server not running?"; 3622 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 3623 | return; 3624 | } 3625 | 3626 | jack_set_process_callback( data->client, jackProcessOut, data ); 3627 | data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); 3628 | data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); 3629 | jack_activate( data->client ); 3630 | 3631 | apiData_ = (void *) data; 3632 | } 3633 | 3634 | MidiOutJack :: ~MidiOutJack() 3635 | { 3636 | JackMidiData *data = static_cast (apiData_); 3637 | closePort(); 3638 | 3639 | // Cleanup 3640 | jack_client_close( data->client ); 3641 | jack_ringbuffer_free( data->buffSize ); 3642 | jack_ringbuffer_free( data->buffMessage ); 3643 | 3644 | delete data; 3645 | } 3646 | 3647 | void MidiOutJack :: openPort( unsigned int portNumber, const std::string portName ) 3648 | { 3649 | JackMidiData *data = static_cast (apiData_); 3650 | 3651 | // Creating new port 3652 | if ( data->port == NULL ) 3653 | data->port = jack_port_register( data->client, portName.c_str(), 3654 | JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); 3655 | 3656 | if ( data->port == NULL ) { 3657 | errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; 3658 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 3659 | } 3660 | 3661 | // Connecting to the output 3662 | std::string name = getPortName( portNumber ); 3663 | jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); 3664 | } 3665 | 3666 | void MidiOutJack :: openVirtualPort( const std::string portName ) 3667 | { 3668 | JackMidiData *data = static_cast (apiData_); 3669 | 3670 | if ( data->port == NULL ) 3671 | data->port = jack_port_register( data->client, portName.c_str(), 3672 | JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); 3673 | 3674 | if ( data->port == NULL ) { 3675 | errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; 3676 | RtMidi::error( RtError::DRIVER_ERROR, errorString_ ); 3677 | } 3678 | } 3679 | 3680 | unsigned int MidiOutJack :: getPortCount() 3681 | { 3682 | int count = 0; 3683 | JackMidiData *data = static_cast (apiData_); 3684 | 3685 | // List of available ports 3686 | const char **ports = jack_get_ports( data->client, NULL, 3687 | JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); 3688 | 3689 | if ( ports == NULL ) return 0; 3690 | while ( ports[count] != NULL ) 3691 | count++; 3692 | 3693 | free( ports ); 3694 | 3695 | return count; 3696 | } 3697 | 3698 | std::string MidiOutJack :: getPortName( unsigned int portNumber ) 3699 | { 3700 | JackMidiData *data = static_cast (apiData_); 3701 | std::ostringstream ost; 3702 | std::string retStr(""); 3703 | 3704 | // List of available ports 3705 | const char **ports = jack_get_ports( data->client, NULL, 3706 | JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); 3707 | 3708 | // Check port validity 3709 | if ( ports == NULL) { 3710 | errorString_ = "MidiOutJack::getPortName: no ports available!"; 3711 | RtMidi::error( RtError::WARNING, errorString_ ); 3712 | return retStr; 3713 | } 3714 | 3715 | if ( ports[portNumber] == NULL) { 3716 | ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; 3717 | errorString_ = ost.str(); 3718 | RtMidi::error( RtError::WARNING, errorString_ ); 3719 | } 3720 | else retStr.assign( ports[portNumber] ); 3721 | 3722 | free( ports ); 3723 | 3724 | return retStr; 3725 | } 3726 | 3727 | void MidiOutJack :: closePort() 3728 | { 3729 | JackMidiData *data = static_cast (apiData_); 3730 | 3731 | if ( data->port == NULL ) return; 3732 | jack_port_unregister( data->client, data->port ); 3733 | data->port = NULL; 3734 | } 3735 | 3736 | void MidiOutJack :: sendMessage( std::vector *message ) 3737 | { 3738 | int nBytes = message->size(); 3739 | JackMidiData *data = static_cast (apiData_); 3740 | 3741 | // Write full message to buffer 3742 | jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0], 3743 | message->size() ); 3744 | jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); 3745 | } 3746 | 3747 | #endif // __UNIX_JACK__ 3748 | -------------------------------------------------------------------------------- /RtMidi/RtMidi.h: -------------------------------------------------------------------------------- 1 | /**********************************************************************/ 2 | /*! \class RtMidi 3 | \brief An abstract base class for realtime MIDI input/output. 4 | 5 | This class implements some common functionality for the realtime 6 | MIDI input/output subclasses RtMidiIn and RtMidiOut. 7 | 8 | RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ 9 | 10 | RtMidi: realtime MIDI i/o C++ classes 11 | Copyright (c) 2003-2012 Gary P. Scavone 12 | 13 | Permission is hereby granted, free of charge, to any person 14 | obtaining a copy of this software and associated documentation files 15 | (the "Software"), to deal in the Software without restriction, 16 | including without limitation the rights to use, copy, modify, merge, 17 | publish, distribute, sublicense, and/or sell copies of the Software, 18 | and to permit persons to whom the Software is furnished to do so, 19 | subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be 22 | included in all copies or substantial portions of the Software. 23 | 24 | Any person wishing to distribute modifications to the Software is 25 | asked to send the modifications to the original developer so that 26 | they can be incorporated into the canonical version. This is, 27 | however, not a binding provision of this license. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 32 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 33 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 34 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | */ 37 | /**********************************************************************/ 38 | 39 | /*! 40 | \file RtMidi.h 41 | */ 42 | 43 | // RtMidi: Version 2.0.1 44 | 45 | #ifndef RTMIDI_H 46 | #define RTMIDI_H 47 | 48 | #include "RtError.h" 49 | #include 50 | #include 51 | 52 | class RtMidi 53 | { 54 | public: 55 | 56 | //! MIDI API specifier arguments. 57 | enum Api { 58 | UNSPECIFIED, /*!< Search for a working compiled API. */ 59 | MACOSX_CORE, /*!< Macintosh OS-X Core Midi API. */ 60 | LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ 61 | UNIX_JACK, /*!< The Jack Low-Latency MIDI Server API. */ 62 | WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ 63 | WINDOWS_KS, /*!< The Microsoft Kernel Streaming MIDI API. */ 64 | RTMIDI_DUMMY /*!< A compilable but non-functional API. */ 65 | }; 66 | 67 | //! A static function to determine the available compiled MIDI APIs. 68 | /*! 69 | The values returned in the std::vector can be compared against 70 | the enumerated list values. Note that there can be more than one 71 | API compiled for certain operating systems. 72 | */ 73 | static void getCompiledApi( std::vector &apis ) throw(); 74 | 75 | //! Pure virtual openPort() function. 76 | virtual void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi" ) ) = 0; 77 | 78 | //! Pure virtual openVirtualPort() function. 79 | virtual void openVirtualPort( const std::string portName = std::string( "RtMidi" ) ) = 0; 80 | 81 | //! Pure virtual getPortCount() function. 82 | virtual unsigned int getPortCount() = 0; 83 | 84 | //! Pure virtual getPortName() function. 85 | virtual std::string getPortName( unsigned int portNumber = 0 ) = 0; 86 | 87 | //! Pure virtual closePort() function. 88 | virtual void closePort( void ) = 0; 89 | 90 | //! A basic error reporting function for RtMidi classes. 91 | static void error( RtError::Type type, std::string errorString ); 92 | 93 | protected: 94 | 95 | RtMidi() {}; 96 | virtual ~RtMidi() {}; 97 | }; 98 | 99 | /**********************************************************************/ 100 | /*! \class RtMidiIn 101 | \brief A realtime MIDI input class. 102 | 103 | This class provides a common, platform-independent API for 104 | realtime MIDI input. It allows access to a single MIDI input 105 | port. Incoming MIDI messages are either saved to a queue for 106 | retrieval using the getMessage() function or immediately passed to 107 | a user-specified callback function. Create multiple instances of 108 | this class to connect to more than one MIDI device at the same 109 | time. With the OS-X and Linux ALSA MIDI APIs, it is also possible 110 | to open a virtual input port to which other MIDI software clients 111 | can connect. 112 | 113 | by Gary P. Scavone, 2003-2012. 114 | */ 115 | /**********************************************************************/ 116 | 117 | // **************************************************************** // 118 | // 119 | // RtMidiIn and RtMidiOut class declarations. 120 | // 121 | // RtMidiIn / RtMidiOut are "controllers" used to select an available 122 | // MIDI input or output interface. They present common APIs for the 123 | // user to call but all functionality is implemented by the classes 124 | // MidiInApi, MidiOutApi and their subclasses. RtMidiIn and RtMidiOut 125 | // each create an instance of a MidiInApi or MidiOutApi subclass based 126 | // on the user's API choice. If no choice is made, they attempt to 127 | // make a "logical" API selection. 128 | // 129 | // **************************************************************** // 130 | 131 | class MidiInApi; 132 | class MidiOutApi; 133 | 134 | class RtMidiIn : public RtMidi 135 | { 136 | public: 137 | 138 | //! User callback function type definition. 139 | typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); 140 | 141 | //! Default constructor that allows an optional api, client name and queue size. 142 | /*! 143 | An exception will be thrown if a MIDI system initialization 144 | error occurs. The queue size defines the maximum number of 145 | messages that can be held in the MIDI queue (when not using a 146 | callback function). If the queue size limit is reached, 147 | incoming messages will be ignored. 148 | 149 | If no API argument is specified and multiple API support has been 150 | compiled, the default order of use is JACK, ALSA (Linux) and CORE, 151 | Jack (OS-X). 152 | */ 153 | RtMidiIn( RtMidi::Api api=UNSPECIFIED, 154 | const std::string clientName = std::string( "RtMidi Input Client"), 155 | unsigned int queueSizeLimit = 100 ); 156 | 157 | //! If a MIDI connection is still open, it will be closed by the destructor. 158 | ~RtMidiIn ( void ) throw(); 159 | 160 | //! Returns the MIDI API specifier for the current instance of RtMidiIn. 161 | RtMidi::Api getCurrentApi( void ) throw(); 162 | 163 | //! Open a MIDI input connection. 164 | /*! 165 | An optional port number greater than 0 can be specified. 166 | Otherwise, the default or first port found is opened. 167 | */ 168 | void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Input" ) ); 169 | 170 | //! Create a virtual input port, with optional name, to allow software connections (OS X and ALSA only). 171 | /*! 172 | This function creates a virtual MIDI input port to which other 173 | software applications can connect. This type of functionality 174 | is currently only supported by the Macintosh OS-X and Linux ALSA 175 | APIs (the function does nothing for the other APIs). 176 | */ 177 | void openVirtualPort( const std::string portName = std::string( "RtMidi Input" ) ); 178 | 179 | //! Set a callback function to be invoked for incoming MIDI messages. 180 | /*! 181 | The callback function will be called whenever an incoming MIDI 182 | message is received. While not absolutely necessary, it is best 183 | to set the callback function before opening a MIDI port to avoid 184 | leaving some messages in the queue. 185 | */ 186 | void setCallback( RtMidiCallback callback, void *userData = 0 ); 187 | 188 | //! Cancel use of the current callback function (if one exists). 189 | /*! 190 | Subsequent incoming MIDI messages will be written to the queue 191 | and can be retrieved with the \e getMessage function. 192 | */ 193 | void cancelCallback(); 194 | 195 | //! Close an open MIDI connection (if one exists). 196 | void closePort( void ); 197 | 198 | //! Return the number of available MIDI input ports. 199 | unsigned int getPortCount(); 200 | 201 | //! Return a string identifier for the specified MIDI input port number. 202 | /*! 203 | An empty string is returned if an invalid port specifier is provided. 204 | */ 205 | std::string getPortName( unsigned int portNumber = 0 ); 206 | 207 | //! Specify whether certain MIDI message types should be queued or ignored during input. 208 | /*! 209 | o By default, MIDI timing and active sensing messages are ignored 210 | during message input because of their relative high data rates. 211 | MIDI sysex messages are ignored by default as well. Variable 212 | values of "true" imply that the respective message type will be 213 | ignored. 214 | */ 215 | void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); 216 | 217 | //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. 218 | /*! 219 | This function returns immediately whether a new message is 220 | available or not. A valid message is indicated by a non-zero 221 | vector size. An exception is thrown if an error occurs during 222 | message retrieval or an input connection was not previously 223 | established. 224 | */ 225 | double getMessage( std::vector *message ); 226 | 227 | protected: 228 | void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ); 229 | MidiInApi *rtapi_; 230 | 231 | }; 232 | 233 | /**********************************************************************/ 234 | /*! \class RtMidiOut 235 | \brief A realtime MIDI output class. 236 | 237 | This class provides a common, platform-independent API for MIDI 238 | output. It allows one to probe available MIDI output ports, to 239 | connect to one such port, and to send MIDI bytes immediately over 240 | the connection. Create multiple instances of this class to 241 | connect to more than one MIDI device at the same time. With the 242 | OS-X and Linux ALSA MIDI APIs, it is also possible to open a 243 | virtual port to which other MIDI software clients can connect. 244 | 245 | by Gary P. Scavone, 2003-2012. 246 | */ 247 | /**********************************************************************/ 248 | 249 | class RtMidiOut : public RtMidi 250 | { 251 | public: 252 | 253 | //! Default constructor that allows an optional client name. 254 | /*! 255 | An exception will be thrown if a MIDI system initialization error occurs. 256 | 257 | If no API argument is specified and multiple API support has been 258 | compiled, the default order of use is JACK, ALSA (Linux) and CORE, 259 | Jack (OS-X). 260 | */ 261 | RtMidiOut( RtMidi::Api api=UNSPECIFIED, 262 | const std::string clientName = std::string( "RtMidi Output Client") ); 263 | 264 | //! The destructor closes any open MIDI connections. 265 | ~RtMidiOut( void ) throw(); 266 | 267 | //! Returns the MIDI API specifier for the current instance of RtMidiOut. 268 | RtMidi::Api getCurrentApi( void ) throw(); 269 | 270 | //! Open a MIDI output connection. 271 | /*! 272 | An optional port number greater than 0 can be specified. 273 | Otherwise, the default or first port found is opened. An 274 | exception is thrown if an error occurs while attempting to make 275 | the port connection. 276 | */ 277 | void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Output" ) ); 278 | 279 | //! Close an open MIDI connection (if one exists). 280 | void closePort( void ); 281 | 282 | //! Create a virtual output port, with optional name, to allow software connections (OS X and ALSA only). 283 | /*! 284 | This function creates a virtual MIDI output port to which other 285 | software applications can connect. This type of functionality 286 | is currently only supported by the Macintosh OS-X and Linux ALSA 287 | APIs (the function does nothing with the other APIs). An 288 | exception is thrown if an error occurs while attempting to create 289 | the virtual port. 290 | */ 291 | void openVirtualPort( const std::string portName = std::string( "RtMidi Output" ) ); 292 | 293 | //! Return the number of available MIDI output ports. 294 | unsigned int getPortCount( void ); 295 | 296 | //! Return a string identifier for the specified MIDI port type and number. 297 | /*! 298 | An empty string is returned if an invalid port specifier is provided. 299 | */ 300 | std::string getPortName( unsigned int portNumber = 0 ); 301 | 302 | //! Immediately send a single message out an open MIDI output port. 303 | /*! 304 | An exception is thrown if an error occurs during output or an 305 | output connection was not previously established. 306 | */ 307 | void sendMessage( std::vector *message ); 308 | 309 | protected: 310 | void openMidiApi( RtMidi::Api api, const std::string clientName ); 311 | MidiOutApi *rtapi_; 312 | }; 313 | 314 | 315 | // **************************************************************** // 316 | // 317 | // MidiInApi / MidiOutApi class declarations. 318 | // 319 | // Subclasses of MidiInApi and MidiOutApi contain all API- and 320 | // OS-specific code necessary to fully implement the RtMidi API. 321 | // 322 | // Note that MidiInApi and MidiOutApi are abstract base classes and 323 | // cannot be explicitly instantiated. RtMidiIn and RtMidiOut will 324 | // create instances of a MidiInApi or MidiOutApi subclass. 325 | // 326 | // **************************************************************** // 327 | 328 | class MidiInApi 329 | { 330 | public: 331 | 332 | MidiInApi( unsigned int queueSizeLimit ); 333 | virtual ~MidiInApi( void ); 334 | virtual RtMidi::Api getCurrentApi( void ) = 0; 335 | virtual void openPort( unsigned int portNumber, const std::string portName ) = 0; 336 | virtual void openVirtualPort( const std::string portName ) = 0; 337 | virtual void closePort( void ) = 0; 338 | void setCallback( RtMidiIn::RtMidiCallback callback, void *userData ); 339 | void cancelCallback( void ); 340 | virtual unsigned int getPortCount( void ) = 0; 341 | virtual std::string getPortName( unsigned int portNumber ) = 0; 342 | virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ); 343 | double getMessage( std::vector *message ); 344 | 345 | // A MIDI structure used internally by the class to store incoming 346 | // messages. Each message represents one and only one MIDI message. 347 | struct MidiMessage { 348 | std::vector bytes; 349 | double timeStamp; 350 | 351 | // Default constructor. 352 | MidiMessage() 353 | :bytes(0), timeStamp(0.0) {} 354 | }; 355 | 356 | struct MidiQueue { 357 | unsigned int front; 358 | unsigned int back; 359 | unsigned int size; 360 | unsigned int ringSize; 361 | MidiMessage *ring; 362 | 363 | // Default constructor. 364 | MidiQueue() 365 | :front(0), back(0), size(0), ringSize(0) {} 366 | }; 367 | 368 | // The RtMidiInData structure is used to pass private class data to 369 | // the MIDI input handling function or thread. 370 | struct RtMidiInData { 371 | MidiQueue queue; 372 | MidiMessage message; 373 | unsigned char ignoreFlags; 374 | bool doInput; 375 | bool firstMessage; 376 | void *apiData; 377 | bool usingCallback; 378 | void *userCallback; 379 | void *userData; 380 | bool continueSysex; 381 | 382 | // Default constructor. 383 | RtMidiInData() 384 | : ignoreFlags(7), doInput(false), firstMessage(true), 385 | apiData(0), usingCallback(false), userCallback(0), userData(0), 386 | continueSysex(false) {} 387 | }; 388 | 389 | protected: 390 | virtual void initialize( const std::string& clientName ) = 0; 391 | RtMidiInData inputData_; 392 | 393 | void *apiData_; 394 | bool connected_; 395 | std::string errorString_; 396 | }; 397 | 398 | class MidiOutApi 399 | { 400 | public: 401 | 402 | MidiOutApi( void ); 403 | virtual ~MidiOutApi( void ); 404 | virtual RtMidi::Api getCurrentApi( void ) = 0; 405 | virtual void openPort( unsigned int portNumber, const std::string portName ) = 0; 406 | virtual void openVirtualPort( const std::string portName ) = 0; 407 | virtual void closePort( void ) = 0; 408 | virtual unsigned int getPortCount( void ) = 0; 409 | virtual std::string getPortName( unsigned int portNumber ) = 0; 410 | virtual void sendMessage( std::vector *message ) = 0; 411 | 412 | protected: 413 | virtual void initialize( const std::string& clientName ) = 0; 414 | 415 | void *apiData_; 416 | bool connected_; 417 | std::string errorString_; 418 | }; 419 | 420 | // **************************************************************** // 421 | // 422 | // Inline RtMidiIn and RtMidiOut definitions. 423 | // 424 | // **************************************************************** // 425 | 426 | inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } 427 | inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { return rtapi_->openPort( portNumber, portName ); } 428 | inline void RtMidiIn :: openVirtualPort( const std::string portName ) { return rtapi_->openVirtualPort( portName ); } 429 | inline void RtMidiIn :: closePort( void ) { return rtapi_->closePort(); } 430 | inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { return rtapi_->setCallback( callback, userData ); } 431 | inline void RtMidiIn :: cancelCallback( void ) { return rtapi_->cancelCallback(); } 432 | inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } 433 | inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } 434 | inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { return rtapi_->ignoreTypes( midiSysex, midiTime, midiSense ); } 435 | inline double RtMidiIn :: getMessage( std::vector *message ) { return rtapi_->getMessage( message ); } 436 | 437 | inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } 438 | inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { return rtapi_->openPort( portNumber, portName ); } 439 | inline void RtMidiOut :: openVirtualPort( const std::string portName ) { return rtapi_->openVirtualPort( portName ); } 440 | inline void RtMidiOut :: closePort( void ) { return rtapi_->closePort(); } 441 | inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } 442 | inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } 443 | inline void RtMidiOut :: sendMessage( std::vector *message ) { return rtapi_->sendMessage( message ); } 444 | 445 | // **************************************************************** // 446 | // 447 | // MidiInApi and MidiOutApi subclass prototypes. 448 | // 449 | // **************************************************************** // 450 | 451 | #if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(__WINDOWS_KS__) 452 | #define __RTMIDI_DUMMY__ 453 | #endif 454 | 455 | #if defined(__MACOSX_CORE__) 456 | 457 | class MidiInCore: public MidiInApi 458 | { 459 | public: 460 | MidiInCore( const std::string clientName, unsigned int queueSizeLimit ); 461 | ~MidiInCore( void ); 462 | RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; 463 | void openPort( unsigned int portNumber, const std::string portName ); 464 | void openVirtualPort( const std::string portName ); 465 | void closePort( void ); 466 | unsigned int getPortCount( void ); 467 | std::string getPortName( unsigned int portNumber ); 468 | 469 | protected: 470 | void initialize( const std::string& clientName ); 471 | }; 472 | 473 | class MidiOutCore: public MidiOutApi 474 | { 475 | public: 476 | MidiOutCore( const std::string clientName ); 477 | ~MidiOutCore( void ); 478 | RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; 479 | void openPort( unsigned int portNumber, const std::string portName ); 480 | void openVirtualPort( const std::string portName ); 481 | void closePort( void ); 482 | unsigned int getPortCount( void ); 483 | std::string getPortName( unsigned int portNumber ); 484 | void sendMessage( std::vector *message ); 485 | 486 | protected: 487 | void initialize( const std::string& clientName ); 488 | }; 489 | 490 | #endif 491 | 492 | #if defined(__UNIX_JACK__) 493 | 494 | class MidiInJack: public MidiInApi 495 | { 496 | public: 497 | MidiInJack( const std::string clientName, unsigned int queueSizeLimit ); 498 | ~MidiInJack( void ); 499 | RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; 500 | void openPort( unsigned int portNumber, const std::string portName ); 501 | void openVirtualPort( const std::string portName ); 502 | void closePort( void ); 503 | unsigned int getPortCount( void ); 504 | std::string getPortName( unsigned int portNumber ); 505 | 506 | protected: 507 | void initialize( const std::string& clientName ); 508 | }; 509 | 510 | class MidiOutJack: public MidiOutApi 511 | { 512 | public: 513 | MidiOutJack( const std::string clientName ); 514 | ~MidiOutJack( void ); 515 | RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; 516 | void openPort( unsigned int portNumber, const std::string portName ); 517 | void openVirtualPort( const std::string portName ); 518 | void closePort( void ); 519 | unsigned int getPortCount( void ); 520 | std::string getPortName( unsigned int portNumber ); 521 | void sendMessage( std::vector *message ); 522 | 523 | protected: 524 | void initialize( const std::string& clientName ); 525 | }; 526 | 527 | #endif 528 | 529 | #if defined(__LINUX_ALSA__) 530 | 531 | class MidiInAlsa: public MidiInApi 532 | { 533 | public: 534 | MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ); 535 | ~MidiInAlsa( void ); 536 | RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; 537 | void openPort( unsigned int portNumber, const std::string portName ); 538 | void openVirtualPort( const std::string portName ); 539 | void closePort( void ); 540 | unsigned int getPortCount( void ); 541 | std::string getPortName( unsigned int portNumber ); 542 | 543 | protected: 544 | void initialize( const std::string& clientName ); 545 | }; 546 | 547 | class MidiOutAlsa: public MidiOutApi 548 | { 549 | public: 550 | MidiOutAlsa( const std::string clientName ); 551 | ~MidiOutAlsa( void ); 552 | RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; 553 | void openPort( unsigned int portNumber, const std::string portName ); 554 | void openVirtualPort( const std::string portName ); 555 | void closePort( void ); 556 | unsigned int getPortCount( void ); 557 | std::string getPortName( unsigned int portNumber ); 558 | void sendMessage( std::vector *message ); 559 | 560 | protected: 561 | void initialize( const std::string& clientName ); 562 | }; 563 | 564 | #endif 565 | 566 | #if defined(__WINDOWS_MM__) 567 | 568 | class MidiInWinMM: public MidiInApi 569 | { 570 | public: 571 | MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ); 572 | ~MidiInWinMM( void ); 573 | RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; 574 | void openPort( unsigned int portNumber, const std::string portName ); 575 | void openVirtualPort( const std::string portName ); 576 | void closePort( void ); 577 | unsigned int getPortCount( void ); 578 | std::string getPortName( unsigned int portNumber ); 579 | 580 | protected: 581 | void initialize( const std::string& clientName ); 582 | }; 583 | 584 | class MidiOutWinMM: public MidiOutApi 585 | { 586 | public: 587 | MidiOutWinMM( const std::string clientName ); 588 | ~MidiOutWinMM( void ); 589 | RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; 590 | void openPort( unsigned int portNumber, const std::string portName ); 591 | void openVirtualPort( const std::string portName ); 592 | void closePort( void ); 593 | unsigned int getPortCount( void ); 594 | std::string getPortName( unsigned int portNumber ); 595 | void sendMessage( std::vector *message ); 596 | 597 | protected: 598 | void initialize( const std::string& clientName ); 599 | }; 600 | 601 | #endif 602 | 603 | #if defined(__WINDOWS_KS__) 604 | 605 | class MidiInWinKS: public MidiInApi 606 | { 607 | public: 608 | MidiInWinKS( const std::string clientName, unsigned int queueSizeLimit ); 609 | ~MidiInWinKS( void ); 610 | RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_KS; }; 611 | void openPort( unsigned int portNumber, const std::string portName ); 612 | void openVirtualPort( const std::string portName ); 613 | void closePort( void ); 614 | unsigned int getPortCount( void ); 615 | std::string getPortName( unsigned int portNumber ); 616 | 617 | protected: 618 | void initialize( const std::string& clientName ); 619 | }; 620 | 621 | class MidiOutWinKS: public MidiOutApi 622 | { 623 | public: 624 | MidiOutWinKS( const std::string clientName ); 625 | ~MidiOutWinKS( void ); 626 | RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_KS; }; 627 | void openPort( unsigned int portNumber, const std::string portName ); 628 | void openVirtualPort( const std::string portName ); 629 | void closePort( void ); 630 | unsigned int getPortCount( void ); 631 | std::string getPortName( unsigned int portNumber ); 632 | void sendMessage( std::vector *message ); 633 | 634 | protected: 635 | void initialize( const std::string& clientName ); 636 | }; 637 | 638 | #endif 639 | 640 | #if defined(__RTMIDI_DUMMY__) 641 | 642 | class MidiInDummy: public MidiInApi 643 | { 644 | public: 645 | MidiInDummy( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; RtMidi::error( RtError::WARNING, errorString_ ); }; 646 | RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }; 647 | void openPort( unsigned int portNumber, const std::string portName ) {}; 648 | void openVirtualPort( const std::string portName ) {}; 649 | void closePort( void ) {}; 650 | unsigned int getPortCount( void ) { return 0; }; 651 | std::string getPortName( unsigned int portNumber ) { return ""; }; 652 | 653 | protected: 654 | void initialize( const std::string& clientName ) {}; 655 | }; 656 | 657 | class MidiOutDummy: public MidiOutApi 658 | { 659 | public: 660 | MidiOutDummy( const std::string clientName ) { errorString_ = "MidiOutDummy: This class provides no functionality."; RtMidi::error( RtError::WARNING, errorString_ ); }; 661 | RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }; 662 | void openPort( unsigned int portNumber, const std::string portName ) {}; 663 | void openVirtualPort( const std::string portName ) {}; 664 | void closePort( void ) {}; 665 | unsigned int getPortCount( void ) { return 0; }; 666 | std::string getPortName( unsigned int portNumber ) { return ""; }; 667 | void sendMessage( std::vector *message ) {}; 668 | 669 | protected: 670 | void initialize( const std::string& clientName ) {}; 671 | }; 672 | 673 | #endif 674 | 675 | #endif 676 | -------------------------------------------------------------------------------- /rtmidi_python.pyx: -------------------------------------------------------------------------------- 1 | from cython.operator import dereference 2 | 3 | from libcpp.string cimport string 4 | from libcpp.vector cimport vector 5 | 6 | 7 | # Init Python threads and GIL, because RtMidi calls Python from native threads. 8 | # See http://permalink.gmane.org/gmane.comp.python.cython.user/5837 9 | cdef extern from "Python.h": 10 | void PyEval_InitThreads() 11 | 12 | PyEval_InitThreads() 13 | 14 | 15 | cdef extern from "RtMidi/RtMidi.h": 16 | ctypedef void (*RtMidiCallback)(double timeStamp, vector[unsigned char]* message, void* userData) 17 | 18 | enum Api "RtMidi::Api": 19 | UNSPECIFIED "RtMidi::UNSPECIFIED" 20 | 21 | cdef cppclass RtMidi: 22 | void openPort(unsigned int portNumber) 23 | void openVirtualPort(string portName) 24 | unsigned int getPortCount() 25 | string getPortName(unsigned int portNumber) 26 | void closePort() 27 | 28 | cdef cppclass RtMidiIn(RtMidi): 29 | RtMidiIn(Api api, string clientName, unsigned int queueSizeLimit) 30 | void setCallback(RtMidiCallback callback, void* userData) 31 | void cancelCallback() 32 | void ignoreTypes(bint midiSysex, bint midiTime, bint midiSense) 33 | double getMessage(vector[unsigned char]* message) 34 | 35 | cdef cppclass RtMidiOut(RtMidi): 36 | RtMidiOut(Api api, string clientName) 37 | void sendMessage(vector[unsigned char]* message) 38 | 39 | 40 | cdef class MidiBase: 41 | cdef RtMidi* baseptr(self): 42 | return NULL 43 | 44 | def open_port(self, port=0): 45 | if isinstance(port, int): 46 | port_number = port 47 | else: 48 | port_number = self.ports.index(port) 49 | 50 | self.baseptr().openPort(port_number) 51 | 52 | def open_virtual_port(self, port_name="RtMidi"): 53 | self.baseptr().openVirtualPort(string(port_name)) 54 | 55 | property ports: 56 | def __get__(self): 57 | return [self.baseptr().getPortName(i).c_str() for i in range(self.baseptr().getPortCount())] 58 | 59 | def close_port(self): 60 | self.baseptr().closePort() 61 | 62 | 63 | cdef void midi_in_callback(double time_stamp, vector[unsigned char]* message_vector, void* py_callback) with gil: 64 | (py_callback)(dereference(message_vector), time_stamp) 65 | 66 | 67 | cdef class MidiIn(MidiBase): 68 | cdef RtMidiIn* thisptr 69 | cdef object py_callback 70 | 71 | def __cinit__(self, client_name="RtMidi Input Client", queue_size_limit=100): 72 | self.thisptr = new RtMidiIn(UNSPECIFIED, string(client_name), queue_size_limit) 73 | self.py_callback = None 74 | 75 | def __dealloc__(self): 76 | del self.thisptr 77 | 78 | cdef RtMidi* baseptr(self): 79 | return self.thisptr 80 | 81 | property callback: 82 | def __get__(self): 83 | return self.py_callback 84 | 85 | def __set__(self, callback): 86 | if self.py_callback is not None: 87 | self.thisptr.cancelCallback() 88 | 89 | self.py_callback = callback 90 | 91 | if self.py_callback is not None: 92 | self.thisptr.setCallback(midi_in_callback, self.py_callback) 93 | 94 | def ignore_types(self, midi_sysex=True, midi_time=True, midi_sense=True): 95 | self.thisptr.ignoreTypes(midi_sysex, midi_time, midi_sense) 96 | 97 | def get_message(self): 98 | cdef vector[unsigned char] message_vector 99 | delta_time = self.thisptr.getMessage(&message_vector) 100 | 101 | if message_vector.empty(): 102 | return None, None 103 | else: 104 | return message_vector, delta_time 105 | 106 | 107 | cdef class MidiOut(MidiBase): 108 | cdef RtMidiOut* thisptr 109 | 110 | def __cinit__(self, client_name="RtMidi Output Client"): 111 | self.thisptr = new RtMidiOut(UNSPECIFIED, string(client_name)) 112 | 113 | def __dealloc__(self): 114 | del self.thisptr 115 | 116 | cdef RtMidi* baseptr(self): 117 | return self.thisptr 118 | 119 | def send_message(self, message): 120 | cdef vector[unsigned char] message_vector = message 121 | self.thisptr.sendMessage(&message_vector) 122 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import distutils 2 | import sys 3 | 4 | if '--from-cython' in sys.argv: 5 | from Cython.Distutils import build_ext 6 | sys.argv.remove('--from-cython') 7 | module_source = 'rtmidi_python.pyx' 8 | else: 9 | from distutils.command.build_ext import build_ext 10 | module_source = 'rtmidi_python.cpp' 11 | 12 | extension_args = {} 13 | 14 | if sys.platform.startswith('linux'): 15 | extension_args = dict( 16 | define_macros=[('__LINUX_ALSA__', None)], 17 | libraries=['asound', 'pthread'] 18 | ) 19 | 20 | if sys.platform == 'darwin': 21 | extension_args = dict( 22 | define_macros=[('__MACOSX_CORE__', None)], 23 | extra_compile_args=['-frtti'], 24 | extra_link_args=[ 25 | '-framework', 'CoreMIDI', 26 | '-framework', 'CoreAudio', 27 | '-framework', 'CoreFoundation' 28 | ] 29 | ) 30 | 31 | if sys.platform == 'win32': 32 | extension_args = dict( 33 | define_macros=[('__WINDOWS_MM__', None)], 34 | libraries=['winmm'] 35 | ) 36 | 37 | rtmidi_module = distutils.extension.Extension( 38 | 'rtmidi_python', 39 | [module_source, 'RtMidi/RtMidi.cpp'], 40 | language='c++', 41 | **extension_args 42 | ) 43 | 44 | distutils.core.setup( 45 | name='rtmidi-python', 46 | version='0.2.2', 47 | description='Python wrapper for RtMidi', 48 | long_description=open('README.rst').read(), 49 | author='Guido Lorenz', 50 | author_email='code@superquadratic.net', 51 | url='https://github.com/superquadratic/rtmidi-python', 52 | cmdclass={'build_ext': build_ext}, 53 | ext_modules=[rtmidi_module], 54 | license='MIT', 55 | classifiers=[ 56 | 'Development Status :: 4 - Beta', 57 | 'Programming Language :: Cython', 58 | 'Topic :: Multimedia :: Sound/Audio :: MIDI', 59 | 'License :: OSI Approved :: MIT License' 60 | ] 61 | ) 62 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import time 4 | import unittest 5 | 6 | import rtmidi_python as rtmidi 7 | 8 | class RtMidiTestCase(unittest.TestCase): 9 | 10 | NOTE_ON = [0x90, 48, 100] 11 | NOTE_OFF = [0x80, 48, 16] 12 | TEST_PORT_NAME = 'rtmidi test port' 13 | DELAY = 0.1 14 | 15 | def setUp(self): 16 | self.midi_out = rtmidi.MidiOut() 17 | self.midi_out.open_virtual_port(self.TEST_PORT_NAME) 18 | 19 | self.midi_in = rtmidi.MidiIn() 20 | self.midi_in.open_port(self.TEST_PORT_NAME) 21 | 22 | def tearDown(self): 23 | self.midi_in.close_port() 24 | self.midi_in = None 25 | self.midi_out.close_port() 26 | self.midi_out = None 27 | 28 | def test_send_and_get_message(self): 29 | self.midi_out.send_message(self.NOTE_ON) 30 | self.midi_out.send_message(self.NOTE_OFF) 31 | time.sleep(self.DELAY) 32 | incoming_message_1, _ = self.midi_in.get_message() 33 | incoming_message_2, _ = self.midi_in.get_message() 34 | self.assertEqual(incoming_message_1, self.NOTE_ON) 35 | self.assertEqual(incoming_message_2, self.NOTE_OFF) 36 | 37 | def test_callback(self): 38 | incoming_messages = [] 39 | def callback(message, time_stamp): 40 | incoming_messages.append(message) 41 | self.midi_in.callback = callback 42 | self.midi_out.send_message(self.NOTE_ON) 43 | self.midi_out.send_message(self.NOTE_OFF) 44 | time.sleep(self.DELAY) 45 | self.assertEqual(incoming_messages, [self.NOTE_ON, self.NOTE_OFF]) 46 | 47 | incoming_messages = [] 48 | self.midi_in.callback = None 49 | self.midi_out.send_message(self.NOTE_ON) 50 | self.midi_out.send_message(self.NOTE_OFF) 51 | time.sleep(self.DELAY) 52 | self.assertEqual(incoming_messages, []) 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | --------------------------------------------------------------------------------