1 /**********************************************************************/
3 \brief An abstract base class for realtime MIDI input/output.
5 This class implements some common functionality for the realtime
6 MIDI input/output subclasses RtMidiIn and RtMidiOut.
8 RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
10 RtMidi: realtime MIDI i/o C++ classes
11 Copyright (c) 2003-2005 Gary P. Scavone
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:
21 The above copyright notice and this permission notice shall be
22 included in all copies or substantial portions of the Software.
24 Any person wishing to distribute modifications to the Software is
25 requested to send the modifications to the original developer so that
26 they can be incorporated into the canonical version.
28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
31 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
32 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
33 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 /**********************************************************************/
38 // RtMidi: Version 1.0.4, 14 October 2005
46 //*********************************************************************//
47 // Common RtMidi Definitions
48 //*********************************************************************//
51 : apiData_( 0 ), connected_( false )
56 void RtMidi :: error( RtError::Type type
)
58 if (type
== RtError::WARNING
) {
59 std::cerr
<< "[chuck](via RtMidi): " << errorString_
<< "\n";
61 else if (type
== RtError::DEBUG_WARNING
) {
62 #if defined(__RTMIDI_DEBUG__)
63 std::cerr
<< "[chuck](via RtMidi): " << errorString_
<< "\n";
67 // std::cerr << "[chuck](via RtMidi): " << errorString_ << "\n";
68 throw RtError( errorString_
, type
);
73 //*********************************************************************//
74 // Common RtMidiIn Definitions
75 //*********************************************************************//
77 RtMidiIn :: RtMidiIn() : RtMidi()
82 void RtMidiIn :: setCallback( RtMidiCallback callback
, void *userData
)
84 if ( inputData_
.usingCallback
) {
85 errorString_
= "RtMidiIn::setCallback: callback function is already set!";
86 error( RtError::WARNING
);
91 errorString_
= "RtMidiIn::setCallback: callback function value is invalid!";
92 error( RtError::WARNING
);
96 inputData_
.userCallback
= (void *) callback
;
97 inputData_
.userData
= userData
;
98 inputData_
.usingCallback
= true;
101 void RtMidiIn :: cancelCallback()
103 if ( !inputData_
.usingCallback
) {
104 errorString_
= "RtMidiIn::cancelCallback: no callback function was set!";
105 error( RtError::WARNING
);
109 inputData_
.userCallback
= 0;
110 inputData_
.userData
= 0;
111 inputData_
.usingCallback
= false;
114 void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize
)
116 inputData_
.queueLimit
= queueSize
;
119 void RtMidiIn :: ignoreTypes( bool midiSysex
, bool midiTime
, bool midiSense
)
121 inputData_
.ignoreFlags
= 0;
122 if ( midiSysex
) inputData_
.ignoreFlags
= 0x01;
123 if ( midiTime
) inputData_
.ignoreFlags
|= 0x02;
124 if ( midiSense
) inputData_
.ignoreFlags
|= 0x04;
127 double RtMidiIn :: getMessage( std::vector
<unsigned char> *message
)
131 if ( inputData_
.usingCallback
) {
132 errorString_
= "RtMidiIn::getNextMessage: user callback is currently set for this port.";
133 error( RtError::WARNING
);
137 if ( inputData_
.queue
.size() == 0 ) return 0.0;
139 // Copy queued message to the vector pointer argument and then "pop" it.
140 std::vector
<unsigned char> *bytes
= &(inputData_
.queue
.front().bytes
);
141 message
->assign( bytes
->begin(), bytes
->end() );
142 double deltaTime
= inputData_
.queue
.front().timeStamp
;
143 inputData_
.queue
.pop();
148 //*********************************************************************//
149 // Common RtMidiOut Definitions
150 //*********************************************************************//
152 RtMidiOut :: RtMidiOut() : RtMidi()
158 //*********************************************************************//
159 // API: Macintosh OS-X
160 //*********************************************************************//
162 // API information found at:
163 // - http://developer. apple .com/audio/pdf/coreaudio.pdf
165 #if defined(__MACOSX_CORE__)
167 // The CoreMIDI API is based on the use of a callback function for
168 // MIDI input. We convert the system specific time stamps to delta
171 // OS-X CoreMIDI header files.
172 #include <CoreMIDI/CoreMIDI.h>
173 #include <CoreAudio/HostTime.h>
175 // CoreMIDI naming helper function prototypes
176 static void readable_name(MIDIEndpointRef end
, char *buffer
, int bufsize
);
177 static int get_device_name(SInt32 uniqueid
, char*buffer
, int bufsize
);
179 // A structure to hold variables related to the CoreMIDI API
181 struct CoreMidiData
{
182 MIDIClientRef client
;
184 MIDIEndpointRef endpoint
;
185 MIDIEndpointRef destinationId
;
186 unsigned long long lastTime
;
194 // coreMIDI port naming helper function
196 This wraps up the code to take a passed-in endpoint and work out
197 a nice human-readable name for it.
199 For the moment, this function will return firstly the device name +
200 port name, or if we can work out what external devices are hooked
201 in, that name. It only grabs the very first connected device name.
203 NB. won't get connected devices correctly before 10.3.
205 static void readable_name(MIDIEndpointRef end
, char *buffer
, int bufsize
)
207 MIDIEntityRef ent
= NULL
;
208 MIDIDeviceRef dev
= NULL
;
209 int ii
, count
, length
, ret
;
211 CFDataRef data
= NULL
;
216 if( MIDIObjectGetDataProperty(end
, kMIDIPropertyConnectionUniqueID
, &data
) == 0)
218 length
= CFDataGetLength(data
) / sizeof(SInt32
);
219 idarray
= (SInt32
*) CFDataGetBytePtr(data
);
221 for (ii
= 0; ii
< length
; ii
++) {
225 strcpy(buffer
, ", ");
230 if (get_device_name(idarray
[ii
], buffer
, bufsize
) == 0) {
232 bufsize
-= strlen(buffer
);
233 buffer
+= strlen(buffer
);
243 // build up the name of the enclosing device, if it can be found.
244 if (MIDIEndpointGetEntity(end
, &ent
) == 0) {
245 if (MIDIEntityGetDevice(ent
, &dev
) == 0) {
246 if (MIDIObjectGetStringProperty(dev
, kMIDIPropertyName
, &s
) == 0) {
247 CFStringGetCString(s
, buffer
, bufsize
, 0);
248 bufsize
-= strlen(buffer
) + 1;
249 buffer
+= strlen(buffer
);
258 // Now add the port/endpoint name.
259 // no need to update buffer pointer. Last item.
260 if (MIDIObjectGetStringProperty(end
, kMIDIPropertyName
, &s
) == 0) {
261 CFStringGetCString(s
, buffy
, 128, 0);
265 if( strcmp( mid
, buffy
) && strlen(buffy
) < bufsize
) {
268 strcpy( buffer
, buffy
);
274 // port naming helper function (External devices)
276 Uses the midiojectfindbyuniqueid function to hunt down the relevant
277 device and copies its name into the buffer provided. We don't know
278 what type of device we'll get back, so we first throw away any
279 external flags (We don't care), and then cast it up to device which
280 is the thing which has a useful name.
282 If it can't find one, return -1. 0 returned on success.
284 static int get_device_name(SInt32 uniqueid
, char *buffer
, int bufsize
)
290 MIDIDeviceRef dev
= NULL
;
291 MIDIEntityRef ent
= NULL
;
292 MIDIEndpointRef end
= NULL
;
293 CFStringRef name
= NULL
;
295 ret
= MIDIObjectFindByUniqueID(uniqueid
, &object
, &type
);
299 // now clear any external flag.
301 type
= type
& (~kMIDIObjectType_ExternalMask
);
303 if (type
== kMIDIObjectType_Device
) {
304 dev
= (MIDIDeviceRef
) object
;
305 } else if (type
== kMIDIObjectType_Entity
) {
306 ent
= (MIDIEntityRef
) object
;
307 if (MIDIEntityGetDevice(ent
, &dev
)) {
310 } if (type
== kMIDIObjectType_Source
||
311 type
== kMIDIObjectType_Destination
) {
312 end
= (MIDIEndpointRef
) object
;
313 if (MIDIEndpointGetEntity(end
, &ent
)) {
316 if (MIDIEntityGetDevice(ent
, &dev
)) {
321 printf("Unknown type %d returned from findobject\n", (int) type
);
326 MIDIObjectGetStringProperty(dev
, kMIDIPropertyName
, &name
);
327 CFStringGetCString(name
, buffer
, bufsize
, 0);
337 //*********************************************************************//
339 // Class Definitions: RtMidiIn
340 //*********************************************************************//
342 void midiInputCallback( const MIDIPacketList
*list
, void *procRef
, void *srcRef
)
344 RtMidiIn::RtMidiInData
*data
= static_cast<RtMidiIn::RtMidiInData
*> (procRef
);
345 CoreMidiData
*apiData
= static_cast<CoreMidiData
*> (data
->apiData
);
347 bool continueSysex
= false;
348 unsigned char status
;
349 unsigned short nBytes
, iByte
, size
;
350 unsigned long long time
;
351 RtMidiIn::MidiMessage message
;
352 const MIDIPacket
*packet
= &list
->packet
[0];
353 for ( unsigned int i
=0; i
<list
->numPackets
; ++i
) {
355 // My interpretation of the CoreMIDI documentation: all message
356 // types, except sysex, are complete within a packet and there may
357 // be several of them in a single packet. Sysex messages can be
358 // broken across multiple packets but are bundled alone within a
359 // packet. I'm assuming that sysex messages, if segmented, must
360 // be complete within the same MIDIPacketList.
362 nBytes
= packet
->length
;
363 if ( nBytes
== 0 ) continue;
365 // Calculate time stamp.
366 message
.timeStamp
= 0.0;
367 if ( data
->firstMessage
)
368 data
->firstMessage
= false;
370 time
= packet
->timeStamp
;
371 time
-= apiData
->lastTime
;
372 time
= AudioConvertHostTimeToNanos( time
);
373 message
.timeStamp
= time
* 0.000000001;
375 apiData
->lastTime
= packet
->timeStamp
;
378 if ( continueSysex
) {
379 // We have a continuing, segmented sysex message.
380 if ( !(data
->ignoreFlags
& 0x01) ) {
381 // If we're not ignoring sysex messages, copy the entire packet.
382 for ( unsigned int j
=0; j
<nBytes
; j
++ )
383 message
.bytes
.push_back( packet
->data
[j
] );
385 if ( packet
->data
[nBytes
] == 0xF7 ) continueSysex
= false;
386 if ( !continueSysex
) {
387 // If not a continuing sysex message, invoke the user callback function or queue the message.
388 if ( data
->usingCallback
&& message
.bytes
.size() > 0 ) {
389 RtMidiIn::RtMidiCallback callback
= (RtMidiIn::RtMidiCallback
) data
->userCallback
;
390 callback( message
.timeStamp
, &message
.bytes
, data
->userData
);
393 // As long as we haven't reached our queue size limit, push the message.
394 if ( data
->queueLimit
> data
->queue
.size() )
395 data
->queue
.push( message
);
397 std::cerr
<< "\nRtMidiIn: message queue limit reached!!\n\n";
399 message
.bytes
.clear();
403 while ( iByte
< nBytes
) {
405 // We are expecting that the next byte in the packet is a status byte.
406 status
= packet
->data
[iByte
];
407 if ( !(status
& 0x80) ) break;
408 // Determine the number of bytes in the MIDI message.
409 if ( status
< 0xC0 ) size
= 3;
410 else if ( status
< 0xE0 ) size
= 2;
411 else if ( status
< 0xF0 ) size
= 3;
412 else if ( status
== 0xF0 ) {
414 if ( data
->ignoreFlags
& 0x01 ) {
418 else size
= nBytes
- iByte
;
419 if ( packet
->data
[nBytes
] == 0xF7 ) continueSysex
= false;
421 else if ( status
< 0xF3 ) {
422 if ( status
== 0xF1 && (data
->ignoreFlags
& 0x02) ) {
423 // A MIDI time code message and we're ignoring it.
429 else if ( status
== 0xF3 ) size
= 2;
430 else if ( status
== 0xF8 ) {
432 if ( data
->ignoreFlags
& 0x02 ) {
433 // A MIDI timing tick message and we're ignoring it.
438 else if ( status
== 0xFE && (data
->ignoreFlags
& 0x04) ) {
439 // A MIDI active sensing message and we're ignoring it.
445 // Copy the MIDI data to our vector.
447 message
.bytes
.assign( &packet
->data
[iByte
], &packet
->data
[iByte
+size
] );
448 if ( !continueSysex
) {
449 // If not a continuing sysex message, invoke the user callback function or queue the message.
450 if ( data
->usingCallback
) {
451 RtMidiIn::RtMidiCallback callback
= (RtMidiIn::RtMidiCallback
) data
->userCallback
;
452 callback( message
.timeStamp
, &message
.bytes
, data
->userData
);
455 // As long as we haven't reached our queue size limit, push the message.
456 if ( data
->queueLimit
> data
->queue
.size() )
457 data
->queue
.push( message
);
459 std::cerr
<< "\nRtMidiIn: message queue limit reached!!\n\n";
461 message
.bytes
.clear();
467 packet
= MIDIPacketNext(packet
);
471 void RtMidiIn :: initialize( void )
473 // Set up our client.
474 MIDIClientRef client
;
475 OSStatus result
= MIDIClientCreate( CFSTR("RtMidi Input Client"), NULL
, NULL
, &client
);
476 if ( result
!= noErr
) {
477 errorString_
= "RtMidiIn::initialize: error creating OS-X MIDI client object.";
478 error( RtError::DRIVER_ERROR
);
481 // Save our api-specific connection information.
482 CoreMidiData
*data
= (CoreMidiData
*) new CoreMidiData
;
483 data
->client
= client
;
485 apiData_
= (void *) data
;
486 inputData_
.apiData
= (void *) data
;
489 void RtMidiIn :: openPort( unsigned int portNumber
)
492 errorString_
= "RtMidiIn::openPort: a valid connection already exists!";
493 error( RtError::WARNING
);
497 unsigned int nSrc
= MIDIGetNumberOfSources();
499 errorString_
= "RtMidiIn::openPort: no MIDI input sources found!";
500 error( RtError::NO_DEVICES_FOUND
);
503 std::ostringstream ost
;
504 if ( portNumber
>= nSrc
) {
505 ost
<< "RtMidiIn::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
506 errorString_
= ost
.str();
507 error( RtError::INVALID_PARAMETER
);
511 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
512 OSStatus result
= MIDIInputPortCreate( data
->client
, CFSTR("RtMidi MIDI Input Port"), midiInputCallback
, (void *)&inputData_
, &port
);
513 if ( result
!= noErr
) {
514 MIDIClientDispose( data
->client
);
515 errorString_
= "RtMidiIn::openPort: error creating OS-X MIDI input port.";
516 error( RtError::DRIVER_ERROR
);
519 // Get the desired input source identifier.
520 MIDIEndpointRef endpoint
= MIDIGetSource( portNumber
);
521 if ( endpoint
== NULL
) {
522 MIDIPortDispose( port
);
523 MIDIClientDispose( data
->client
);
524 errorString_
= "RtMidiIn::openPort: error getting MIDI input source reference.";
525 error( RtError::DRIVER_ERROR
);
528 // Make the connection.
529 result
= MIDIPortConnectSource( port
, endpoint
, NULL
);
530 if ( result
!= noErr
) {
531 MIDIPortDispose( port
);
532 MIDIClientDispose( data
->client
);
533 errorString_
= "RtMidiIn::openPort: error connecting OS-X MIDI input port.";
534 error( RtError::DRIVER_ERROR
);
537 // Save our api-specific port information.
543 void RtMidiIn :: openVirtualPort()
545 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
547 // Create a virtual MIDI input destination.
548 MIDIEndpointRef endpoint
;
549 OSStatus result
= MIDIDestinationCreate( data
->client
, CFSTR("RtMidi Input"), midiInputCallback
, (void *)&inputData_
, &endpoint
);
550 if ( result
!= noErr
) {
551 errorString_
= "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination.";
552 error( RtError::DRIVER_ERROR
);
555 // Save our api-specific connection information.
556 data
->endpoint
= endpoint
;
559 void RtMidiIn :: closePort( void )
562 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
563 MIDIPortDispose( data
->port
);
568 RtMidiIn :: ~RtMidiIn()
570 // Close a connection if it exists.
574 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
575 MIDIClientDispose( data
->client
);
576 if ( data
->endpoint
) MIDIEndpointDispose( data
->endpoint
);
580 unsigned int RtMidiIn :: getPortCount()
582 return MIDIGetNumberOfSources();
585 std::string
RtMidiIn :: getPortName( unsigned int portNumber
)
588 MIDIEndpointRef portRef
;
589 std::ostringstream ost
;
592 if ( portNumber
>= MIDIGetNumberOfSources() ) {
593 ost
<< "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber
<< ") is invalid.";
594 errorString_
= ost
.str();
595 error( RtError::INVALID_PARAMETER
);
597 portRef
= MIDIGetSource( portNumber
);
598 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
599 readable_name(portRef
, name
, sizeof(name
));
601 MIDIObjectGetStringProperty( portRef
, kMIDIPropertyName
, &nameRef
);
602 CFStringGetCString( nameRef
, name
, sizeof(name
), 0);
603 CFRelease( nameRef
);
605 std::string stringName
= name
;
609 //*********************************************************************//
611 // Class Definitions: RtMidiOut
612 //*********************************************************************//
614 unsigned int RtMidiOut :: getPortCount()
616 return MIDIGetNumberOfDestinations();
619 std::string
RtMidiOut :: getPortName( unsigned int portNumber
)
622 MIDIEndpointRef portRef
;
623 std::ostringstream ost
;
626 if ( portNumber
>= MIDIGetNumberOfDestinations() ) {
627 ost
<< "RtMidiOut::getPortName: 'portNumber' argument (" << portNumber
<< ") is invalid.";
628 errorString_
= ost
.str();
629 error( RtError::INVALID_PARAMETER
);
631 portRef
= MIDIGetDestination( portNumber
);
632 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
633 readable_name(portRef
, name
, sizeof(name
));
635 MIDIObjectGetStringProperty( portRef
, kMIDIPropertyName
, &nameRef
);
636 CFStringGetCString( nameRef
, name
, sizeof(name
), 0);
637 CFRelease( nameRef
);
639 std::string stringName
= name
;
643 void RtMidiOut :: initialize( void )
645 // Set up our client.
646 MIDIClientRef client
;
647 OSStatus result
= MIDIClientCreate( CFSTR("RtMidi Output Client"), NULL
, NULL
, &client
);
648 if ( result
!= noErr
) {
649 errorString_
= "RtMidiOut::initialize: error creating OS-X MIDI client object.";
650 error( RtError::DRIVER_ERROR
);
653 // Save our api-specific connection information.
654 CoreMidiData
*data
= (CoreMidiData
*) new CoreMidiData
;
655 data
->client
= client
;
657 apiData_
= (void *) data
;
660 void RtMidiOut :: openPort( unsigned int portNumber
)
663 errorString_
= "RtMidiOut::openPort: a valid connection already exists!";
664 error( RtError::WARNING
);
668 unsigned int nDest
= MIDIGetNumberOfDestinations();
670 errorString_
= "RtMidiOut::openPort: no MIDI output destinations found!";
671 error( RtError::NO_DEVICES_FOUND
);
674 std::ostringstream ost
;
675 if ( portNumber
>= nDest
) {
676 ost
<< "RtMidiOut::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
677 errorString_
= ost
.str();
678 error( RtError::INVALID_PARAMETER
);
682 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
683 OSStatus result
= MIDIOutputPortCreate( data
->client
, CFSTR("RtMidi Virtual MIDI Output Port"), &port
);
684 if ( result
!= noErr
) {
685 MIDIClientDispose( data
->client
);
686 errorString_
= "RtMidiOut::openPort: error creating OS-X MIDI output port.";
687 error( RtError::DRIVER_ERROR
);
690 // Get the desired output port identifier.
691 MIDIEndpointRef destination
= MIDIGetDestination( portNumber
);
692 if ( destination
== NULL
) {
693 MIDIPortDispose( port
);
694 MIDIClientDispose( data
->client
);
695 errorString_
= "RtMidiOut::openPort: error getting MIDI output destination reference.";
696 error( RtError::DRIVER_ERROR
);
699 // Save our api-specific connection information.
701 data
->destinationId
= destination
;
705 void RtMidiOut :: closePort( void )
708 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
709 MIDIPortDispose( data
->port
);
714 void RtMidiOut :: openVirtualPort()
716 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
718 if ( data
->endpoint
) {
719 errorString_
= "RtMidiOut::openVirtualPort: a virtual output port already exists!";
720 error( RtError::WARNING
);
724 // Create a virtual MIDI output source.
725 MIDIEndpointRef endpoint
;
726 OSStatus result
= MIDISourceCreate( data
->client
, CFSTR("RtMidi Output"), &endpoint
);
727 if ( result
!= noErr
) {
728 errorString_
= "RtMidiOut::initialize: error creating OS-X virtual MIDI source.";
729 error( RtError::DRIVER_ERROR
);
732 // Save our api-specific connection information.
733 data
->endpoint
= endpoint
;
736 RtMidiOut :: ~RtMidiOut()
738 // Close a connection if it exists.
742 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
743 MIDIClientDispose( data
->client
);
744 if ( data
->endpoint
) MIDIEndpointDispose( data
->endpoint
);
748 void RtMidiOut :: sendMessage( std::vector
<unsigned char> *message
)
750 unsigned int nBytes
= message
->size();
751 // Pad the buffer for extra (unknown) structure data.
752 Byte buffer
[nBytes
+32];
753 MIDIPacketList
*pktlist
= (MIDIPacketList
*) buffer
;
754 MIDIPacket
*curPacket
= MIDIPacketListInit( pktlist
);
756 MIDITimeStamp timeStamp
= 0;
757 curPacket
= MIDIPacketListAdd( pktlist
, sizeof(buffer
), curPacket
, timeStamp
, nBytes
, &message
->at(0) );
759 CoreMidiData
*data
= static_cast<CoreMidiData
*> (apiData_
);
761 // Send to any destinations that may have connected to us.
763 if ( data
->endpoint
) {
764 result
= MIDIReceived( data
->endpoint
, pktlist
);
765 if ( result
!= noErr
) {
766 errorString_
= "RtMidiOut::sendMessage: error sending MIDI to virtual destinations.";
767 error( RtError::WARNING
);
771 // And send to an explicit destination port if we're connected.
773 result
= MIDISend( data
->port
, data
->destinationId
, pktlist
);
774 if ( result
!= noErr
) {
775 errorString_
= "RtMidiOut::sendMessage: error sending MIDI message to port.";
776 error( RtError::WARNING
);
781 #endif // __MACOSX_CORE__
784 //*********************************************************************//
785 // API: LINUX ALSA SEQUENCER
786 //*********************************************************************//
788 // API information found at:
789 // - http://www.alsa-project.org/documentation.php#Library
792 #if defined(__LINUX_ALSASEQ__) || defined(__LINUX_ALSA__) || defined(__LINUX_JACK__)
794 // The ALSA Sequencer API is based on the use of a callback function for
797 // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
798 // time stamps and other assorted fixes!!!
801 #include <sys/time.h>
804 #include <alsa/asoundlib.h>
806 // A structure to hold variables related to the ALSA API
808 struct AlsaMidiData
{
811 snd_seq_port_subscribe_t
*subscription
;
812 snd_midi_event_t
*coder
;
813 unsigned int bufferSize
;
814 unsigned char *buffer
;
816 unsigned long long lastTime
;
817 int queue_id
; // an input queue is needed to get timestamped events
820 #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
822 //*********************************************************************//
824 // Class Definitions: RtMidiIn
825 //*********************************************************************//
827 extern "C" void *alsaMidiHandler( void *ptr
)
829 RtMidiIn::RtMidiInData
*data
= static_cast<RtMidiIn::RtMidiInData
*> (ptr
);
830 AlsaMidiData
*apiData
= static_cast<AlsaMidiData
*> (data
->apiData
);
833 unsigned long long time
, lastTime
;
834 bool continueSysex
= false;
835 RtMidiIn::MidiMessage message
;
839 apiData
->bufferSize
= 32;
840 result
= snd_midi_event_new( 0, &apiData
->coder
);
842 data
->doInput
= false;
843 std::cerr
<< "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n";
846 unsigned char *buffer
= (unsigned char *) malloc( apiData
->bufferSize
);
847 if ( buffer
== NULL
) {
848 data
->doInput
= false;
849 std::cerr
<< "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n";
852 snd_midi_event_init( apiData
->coder
);
853 snd_midi_event_no_status( apiData
->coder
, 1 ); // suppress running status messages
855 while ( data
->doInput
) {
857 if ( snd_seq_event_input_pending( apiData
->seq
, 1 ) == 0 ) {
858 // No data pending ... sleep a bit.
863 // If here, there should be data.
864 result
= snd_seq_event_input( apiData
->seq
, &ev
);
865 if ( result
== -ENOSPC
) {
866 std::cerr
<< "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n";
869 else if ( result
<= 0 ) {
870 std::cerr
<< "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n";
874 // This is a bit weird, but we now have to decode an ALSA MIDI
875 // event (back) into MIDI bytes. We'll ignore non-MIDI types.
876 message
.bytes
.clear();
877 switch ( ev
->type
) {
879 case SND_SEQ_EVENT_PORT_SUBSCRIBED
:
880 #if defined(__RTMIDI_DEBUG__)
881 std::cout
<< "RtMidiIn::alsaMidiHandler: port connection made!\n";
885 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED
:
886 std::cerr
<< "RtMidiIn::alsaMidiHandler: port connection has closed!\n";
887 data
->doInput
= false;
890 case SND_SEQ_EVENT_QFRAME
: // MIDI time code
891 if ( data
->ignoreFlags
& 0x02 ) break;
893 case SND_SEQ_EVENT_TICK
: // MIDI timing tick
894 if ( data
->ignoreFlags
& 0x02 ) break;
896 case SND_SEQ_EVENT_SENSING
: // Active sensing
897 if ( data
->ignoreFlags
& 0x04 ) break;
899 case SND_SEQ_EVENT_SYSEX
:
900 if ( (data
->ignoreFlags
& 0x01) ) break;
901 if ( ev
->data
.ext
.len
> apiData
->bufferSize
) {
902 apiData
->bufferSize
= ev
->data
.ext
.len
;
904 buffer
= (unsigned char *) malloc( apiData
->bufferSize
);
905 if ( buffer
== NULL
) {
906 data
->doInput
= false;
907 std::cerr
<< "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n";
913 nBytes
= snd_midi_event_decode( apiData
->coder
, buffer
, apiData
->bufferSize
, ev
);
915 #if defined(__RTMIDI_DEBUG__)
916 std::cerr
<< "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
921 // The ALSA sequencer has a maximum buffer size for MIDI sysex
922 // events of 256 bytes. If a device sends sysex messages larger
923 // than this, they are segmented into 256 byte chunks. So,
924 // we'll watch for this and concatenate sysex chunks into a
925 // single sysex message if necessary.
926 if ( !continueSysex
)
927 message
.bytes
.assign( buffer
, &buffer
[nBytes
] );
929 message
.bytes
.insert( message
.bytes
.end(), buffer
, &buffer
[nBytes
] );
931 if ( ev
->type
== SND_SEQ_EVENT_SYSEX
&& message
.bytes
.back() == 0xF7 )
932 continueSysex
= false;
934 continueSysex
= true;
938 // Calculate the time stamp:
939 message
.timeStamp
= 0.0;
941 // Method 1: Use the system time.
942 //(void)gettimeofday(&tv, (struct timezone *)NULL);
943 //time = (tv.tv_sec * 1000000) + tv.tv_usec;
945 // Method 2: Use the ALSA sequencer event time data.
946 // (thanks to Pedro Lopez-Cabanillas!).
947 time
= ( ev
->time
.time
.tv_sec
* 1000000 ) + ( ev
->time
.time
.tv_nsec
/1000 );
949 time
-= apiData
->lastTime
;
950 apiData
->lastTime
= lastTime
;
951 if ( data
->firstMessage
== true )
952 data
->firstMessage
= false;
954 message
.timeStamp
= time
* 0.000001;
957 snd_seq_free_event(ev
);
958 if ( message
.bytes
.size() == 0 ) continue;
960 if ( data
->usingCallback
) {
961 RtMidiIn::RtMidiCallback callback
= (RtMidiIn::RtMidiCallback
) data
->userCallback
;
962 callback( message
.timeStamp
, &message
.bytes
, data
->userData
);
965 // As long as we haven't reached our queue size limit, push the message.
966 if ( data
->queueLimit
> data
->queue
.size() )
967 data
->queue
.push( message
);
969 std::cerr
<< "\nRtMidiIn: message queue limit reached!!\n\n";
973 if ( buffer
) free( buffer
);
974 snd_midi_event_free( apiData
->coder
);
979 void RtMidiIn :: initialize( void )
981 // Set up the ALSA sequencer client.
983 int result
= snd_seq_open(&seq
, "default", SND_SEQ_OPEN_DUPLEX
, SND_SEQ_NONBLOCK
);
985 errorString_
= "RtMidiIn::initialize: error creating ALSA sequencer input client object.";
986 error( RtError::DRIVER_ERROR
);
990 snd_seq_set_client_name(seq
, "RtMidi Input Client");
992 // Save our api-specific connection information.
993 AlsaMidiData
*data
= (AlsaMidiData
*) new AlsaMidiData
;
996 apiData_
= (void *) data
;
997 inputData_
.apiData
= (void *) data
;
999 // Create the input queue
1000 data
->queue_id
= snd_seq_alloc_named_queue(seq
, "RtMidi Queue");
1001 // Set arbitrary tempo (mm=100) and resolution (240)
1002 snd_seq_queue_tempo_t
*qtempo
;
1003 snd_seq_queue_tempo_alloca(&qtempo
);
1004 snd_seq_queue_tempo_set_tempo(qtempo
, 600000);
1005 snd_seq_queue_tempo_set_ppq(qtempo
, 240);
1006 snd_seq_set_queue_tempo(data
->seq
, data
->queue_id
, qtempo
);
1007 snd_seq_drain_output(data
->seq
);
1010 // This function is used to count or get the pinfo structure for a given port number.
1011 unsigned int portInfo( snd_seq_t
*seq
, snd_seq_port_info_t
*pinfo
, unsigned int type
, int portNumber
)
1013 snd_seq_client_info_t
*cinfo
;
1016 snd_seq_client_info_alloca( &cinfo
);
1018 snd_seq_client_info_set_client( cinfo
, -1 );
1019 while ( snd_seq_query_next_client( seq
, cinfo
) >= 0 ) {
1020 client
= snd_seq_client_info_get_client( cinfo
);
1021 if ( client
== 0 ) continue;
1023 snd_seq_port_info_set_client( pinfo
, client
);
1024 snd_seq_port_info_set_port( pinfo
, -1 );
1025 while ( snd_seq_query_next_port( seq
, pinfo
) >= 0 ) {
1026 if ( !PORT_TYPE( pinfo
, type
) ) continue;
1027 if ( count
== portNumber
) return 1;
1032 // If a negative portNumber was used, return the port count.
1033 if ( portNumber
< 0 ) return count
;
1037 void RtMidiIn :: openPort( unsigned int portNumber
)
1040 errorString_
= "RtMidiIn::openPort: a valid connection already exists!";
1041 error( RtError::WARNING
);
1045 unsigned int nSrc
= this->getPortCount();
1047 errorString_
= "RtMidiIn::openPort: no MIDI input sources found!";
1048 error( RtError::NO_DEVICES_FOUND
);
1051 snd_seq_port_info_t
*pinfo
;
1052 snd_seq_port_info_alloca( &pinfo
);
1053 std::ostringstream ost
;
1054 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1055 if ( portInfo( data
->seq
, pinfo
, SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
, (int) portNumber
) == 0 ) {
1056 ost
<< "RtMidiIn::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
1057 errorString_
= ost
.str();
1058 error( RtError::INVALID_PARAMETER
);
1062 snd_seq_addr_t sender
, receiver
;
1063 sender
.client
= snd_seq_port_info_get_client( pinfo
);
1064 sender
.port
= snd_seq_port_info_get_port( pinfo
);
1065 receiver
.client
= snd_seq_client_id( data
->seq
);
1066 if ( data
->vport
< 0 ) {
1067 snd_seq_port_info_set_client( pinfo
, 0 );
1068 snd_seq_port_info_set_port( pinfo
, 0 );
1069 snd_seq_port_info_set_capability( pinfo
,
1070 SND_SEQ_PORT_CAP_WRITE
|
1071 SND_SEQ_PORT_CAP_SUBS_WRITE
);
1072 snd_seq_port_info_set_type( pinfo
,
1073 SND_SEQ_PORT_TYPE_MIDI_GENERIC
|
1074 SND_SEQ_PORT_TYPE_APPLICATION
);
1075 snd_seq_port_info_set_midi_channels(pinfo
, 16);
1076 snd_seq_port_info_set_timestamping(pinfo
, 1);
1077 snd_seq_port_info_set_timestamp_real(pinfo
, 1);
1078 snd_seq_port_info_set_timestamp_queue(pinfo
, data
->queue_id
);
1079 snd_seq_port_info_set_name(pinfo
, "RtMidi Input");
1080 data
->vport
= snd_seq_create_port(data
->seq
, pinfo
);
1082 if ( data
->vport
< 0 ) {
1083 errorString_
= "RtMidiIn::openPort: ALSA error creating input port.";
1084 error( RtError::DRIVER_ERROR
);
1088 receiver
.port
= data
->vport
;
1090 // Make subscription
1091 snd_seq_port_subscribe_malloc( &data
->subscription
);
1092 snd_seq_port_subscribe_set_sender(data
->subscription
, &sender
);
1093 snd_seq_port_subscribe_set_dest(data
->subscription
, &receiver
);
1094 if ( snd_seq_subscribe_port(data
->seq
, data
->subscription
) ) {
1095 errorString_
= "RtMidiIn::openPort: ALSA error making port connection.";
1096 error( RtError::DRIVER_ERROR
);
1099 if ( inputData_
.doInput
== false ) {
1100 // Start the input queue
1101 snd_seq_start_queue( data
->seq
, data
->queue_id
, NULL
);
1102 snd_seq_drain_output( data
->seq
);
1103 // Start our MIDI input thread.
1104 pthread_attr_t attr
;
1105 pthread_attr_init(&attr
);
1107 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1108 //pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
1110 inputData_
.doInput
= true;
1111 int err
= pthread_create(&data
->thread
, &attr
, alsaMidiHandler
, &inputData_
);
1112 pthread_attr_destroy(&attr
);
1114 snd_seq_unsubscribe_port( data
->seq
, data
->subscription
);
1115 snd_seq_port_subscribe_free( data
->subscription
);
1116 inputData_
.doInput
= false;
1117 errorString_
= "RtMidiIn::openPort: error starting MIDI input thread!";
1118 error( RtError::THREAD_ERROR
);
1125 void RtMidiIn :: openVirtualPort()
1127 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1128 if ( data
->vport
< 0 ) {
1129 snd_seq_port_info_t
*pinfo
;
1130 snd_seq_port_info_alloca( &pinfo
);
1131 snd_seq_port_info_set_capability( pinfo
,
1132 SND_SEQ_PORT_CAP_WRITE
|
1133 SND_SEQ_PORT_CAP_SUBS_WRITE
);
1134 snd_seq_port_info_set_type( pinfo
,
1135 SND_SEQ_PORT_TYPE_MIDI_GENERIC
|
1136 SND_SEQ_PORT_TYPE_APPLICATION
);
1137 snd_seq_port_info_set_midi_channels(pinfo
, 16);
1138 snd_seq_port_info_set_timestamping(pinfo
, 1);
1139 snd_seq_port_info_set_timestamp_real(pinfo
, 1);
1140 snd_seq_port_info_set_timestamp_queue(pinfo
, data
->queue_id
);
1141 snd_seq_port_info_set_name(pinfo
, "RtMidi Input");
1142 data
->vport
= snd_seq_create_port(data
->seq
, pinfo
);
1144 if ( data
->vport
< 0 ) {
1145 errorString_
= "RtMidiIn::openVirtualPort: ALSA error creating virtual port.";
1146 error( RtError::DRIVER_ERROR
);
1150 if ( inputData_
.doInput
== false ) {
1151 // Start the input queue
1152 snd_seq_start_queue( data
->seq
, data
->queue_id
, NULL
);
1153 snd_seq_drain_output( data
->seq
);
1154 // Start our MIDI input thread.
1155 pthread_attr_t attr
;
1156 pthread_attr_init(&attr
);
1158 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1159 //pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
1161 inputData_
.doInput
= true;
1162 int err
= pthread_create(&data
->thread
, &attr
, alsaMidiHandler
, &inputData_
);
1163 pthread_attr_destroy(&attr
);
1165 snd_seq_unsubscribe_port( data
->seq
, data
->subscription
);
1166 snd_seq_port_subscribe_free( data
->subscription
);
1167 inputData_
.doInput
= false;
1168 errorString_
= "RtMidiIn::openPort: error starting MIDI input thread!";
1169 error( RtError::THREAD_ERROR
);
1174 void RtMidiIn :: closePort( void )
1177 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1178 snd_seq_unsubscribe_port( data
->seq
, data
->subscription
);
1179 snd_seq_port_subscribe_free( data
->subscription
);
1180 // Stop the input queue
1181 snd_seq_stop_queue( data
->seq
, data
->queue_id
, NULL
);
1182 snd_seq_drain_output( data
->seq
);
1187 RtMidiIn :: ~RtMidiIn()
1189 // Close a connection if it exists.
1192 // Shutdown the input thread.
1193 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1194 if ( inputData_
.doInput
) {
1195 inputData_
.doInput
= false;
1196 pthread_join( data
->thread
, NULL
);
1200 if ( data
->vport
>= 0 ) snd_seq_delete_port( data
->seq
, data
->vport
);
1201 snd_seq_free_queue( data
->seq
, data
->queue_id
);
1202 snd_seq_close( data
->seq
);
1206 unsigned int RtMidiIn :: getPortCount()
1208 snd_seq_port_info_t
*pinfo
;
1209 snd_seq_port_info_alloca( &pinfo
);
1211 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1212 return portInfo( data
->seq
, pinfo
, SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
, -1 );
1215 std::string
RtMidiIn :: getPortName( unsigned int portNumber
)
1217 snd_seq_port_info_t
*pinfo
;
1218 snd_seq_port_info_alloca( &pinfo
);
1220 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1221 if ( portInfo( data
->seq
, pinfo
, SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
, (int) portNumber
) ) {
1222 std::string stringName
= std::string( snd_seq_port_info_get_name( pinfo
) );
1226 // If we get here, we didn't find a match.
1227 errorString_
= "RtMidiIn::getPortName: error looking for port name!";
1228 error( RtError::INVALID_PARAMETER
);
1232 //*********************************************************************//
1234 // Class Definitions: RtMidiOut
1235 //*********************************************************************//
1237 unsigned int RtMidiOut :: getPortCount()
1239 snd_seq_port_info_t
*pinfo
;
1240 snd_seq_port_info_alloca( &pinfo
);
1242 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1243 return portInfo( data
->seq
, pinfo
, SND_SEQ_PORT_CAP_WRITE
|SND_SEQ_PORT_CAP_SUBS_WRITE
, -1 );
1246 std::string
RtMidiOut :: getPortName( unsigned int portNumber
)
1248 snd_seq_port_info_t
*pinfo
;
1249 snd_seq_port_info_alloca( &pinfo
);
1251 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1252 if ( portInfo( data
->seq
, pinfo
, SND_SEQ_PORT_CAP_WRITE
|SND_SEQ_PORT_CAP_SUBS_WRITE
, (int) portNumber
) ) {
1253 std::string stringName
= std::string( snd_seq_port_info_get_name( pinfo
) );
1257 // If we get here, we didn't find a match.
1258 errorString_
= "RtMidiOut::getPortName: error looking for port name!";
1259 error( RtError::INVALID_PARAMETER
);
1263 void RtMidiOut :: initialize( void )
1265 // Set up the ALSA sequencer client.
1267 int result
= snd_seq_open(&seq
, "default", SND_SEQ_OPEN_OUTPUT
, 0);
1269 errorString_
= "RtMidiOut::initialize: error creating ALSA sequencer client object.";
1270 error( RtError::DRIVER_ERROR
);
1274 snd_seq_set_client_name(seq
, "RtMidi Output Client");
1276 // Save our api-specific connection information.
1277 AlsaMidiData
*data
= (AlsaMidiData
*) new AlsaMidiData
;
1280 data
->bufferSize
= 32;
1283 result
= snd_midi_event_new( data
->bufferSize
, &data
->coder
);
1286 errorString_
= "RtMidiOut::initialize: error initializing MIDI event parser!\n\n";
1287 error( RtError::DRIVER_ERROR
);
1289 data
->buffer
= (unsigned char *) malloc( data
->bufferSize
);
1290 if ( data
->buffer
== NULL
) {
1292 errorString_
= "RtMidiOut::initialize: error allocating buffer memory!\n\n";
1293 error( RtError::MEMORY_ERROR
);
1295 snd_midi_event_init( data
->coder
);
1296 apiData_
= (void *) data
;
1299 void RtMidiOut :: openPort( unsigned int portNumber
)
1302 errorString_
= "RtMidiOut::openPort: a valid connection already exists!";
1303 error( RtError::WARNING
);
1307 unsigned int nSrc
= this->getPortCount();
1309 errorString_
= "RtMidiOut::openPort: no MIDI output sources found!";
1310 error( RtError::NO_DEVICES_FOUND
);
1313 snd_seq_port_info_t
*pinfo
;
1314 snd_seq_port_info_alloca( &pinfo
);
1315 std::ostringstream ost
;
1316 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1317 if ( portInfo( data
->seq
, pinfo
, SND_SEQ_PORT_CAP_WRITE
|SND_SEQ_PORT_CAP_SUBS_WRITE
, (int) portNumber
) == 0 ) {
1318 ost
<< "RtMidiOut::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
1319 errorString_
= ost
.str();
1320 error( RtError::INVALID_PARAMETER
);
1323 snd_seq_addr_t sender
, receiver
;
1324 receiver
.client
= snd_seq_port_info_get_client( pinfo
);
1325 receiver
.port
= snd_seq_port_info_get_port( pinfo
);
1326 sender
.client
= snd_seq_client_id( data
->seq
);
1328 if ( data
->vport
< 0 ) {
1329 data
->vport
= snd_seq_create_simple_port( data
->seq
, "RtMidi Output",
1330 SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
,
1331 SND_SEQ_PORT_TYPE_MIDI_GENERIC
);
1332 if ( data
->vport
< 0 ) {
1333 errorString_
= "RtMidiOut::openPort: ALSA error creating output port.";
1334 error( RtError::DRIVER_ERROR
);
1338 sender
.port
= data
->vport
;
1340 // Make subscription
1341 snd_seq_port_subscribe_malloc( &data
->subscription
);
1342 snd_seq_port_subscribe_set_sender(data
->subscription
, &sender
);
1343 snd_seq_port_subscribe_set_dest(data
->subscription
, &receiver
);
1344 snd_seq_port_subscribe_set_time_update(data
->subscription
, 1);
1345 snd_seq_port_subscribe_set_time_real(data
->subscription
, 1);
1346 if ( snd_seq_subscribe_port(data
->seq
, data
->subscription
) ) {
1347 errorString_
= "RtMidiOut::openPort: ALSA error making port connection.";
1348 error( RtError::DRIVER_ERROR
);
1354 void RtMidiOut :: closePort( void )
1357 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1358 snd_seq_unsubscribe_port( data
->seq
, data
->subscription
);
1359 snd_seq_port_subscribe_free( data
->subscription
);
1364 void RtMidiOut :: openVirtualPort()
1366 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1367 if ( data
->vport
< 0 ) {
1368 data
->vport
= snd_seq_create_simple_port( data
->seq
, "RtMidi Output",
1369 SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_SUBS_READ
,
1370 SND_SEQ_PORT_TYPE_MIDI_GENERIC
);
1372 if ( data
->vport
< 0 ) {
1373 errorString_
= "RtMidiOut::openVirtualPort: ALSA error creating virtual port.";
1374 error( RtError::DRIVER_ERROR
);
1379 RtMidiOut :: ~RtMidiOut()
1381 // Close a connection if it exists.
1385 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1386 if ( data
->vport
>= 0 ) snd_seq_delete_port( data
->seq
, data
->vport
);
1387 if ( data
->coder
) snd_midi_event_free( data
->coder
);
1388 if ( data
->buffer
) free( data
->buffer
);
1389 snd_seq_close( data
->seq
);
1393 void RtMidiOut :: sendMessage( std::vector
<unsigned char> *message
)
1396 AlsaMidiData
*data
= static_cast<AlsaMidiData
*> (apiData_
);
1397 unsigned int nBytes
= message
->size();
1398 if ( nBytes
> data
->bufferSize
) {
1399 data
->bufferSize
= nBytes
;
1400 result
= snd_midi_event_resize_buffer ( data
->coder
, nBytes
);
1401 if ( result
!= 0 ) {
1402 errorString_
= "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer.";
1403 error( RtError::DRIVER_ERROR
);
1405 free (data
->buffer
);
1406 data
->buffer
= (unsigned char *) malloc( data
->bufferSize
);
1407 if ( data
->buffer
== NULL
) {
1408 errorString_
= "RtMidiOut::initialize: error allocating buffer memory!\n\n";
1409 error( RtError::MEMORY_ERROR
);
1414 snd_seq_ev_clear(&ev
);
1415 snd_seq_ev_set_source(&ev
, data
->vport
);
1416 snd_seq_ev_set_subs(&ev
);
1417 snd_seq_ev_set_direct(&ev
);
1418 for ( unsigned int i
=0; i
<nBytes
; i
++ ) data
->buffer
[i
] = message
->at(i
);
1419 result
= snd_midi_event_encode( data
->coder
, data
->buffer
, (long)nBytes
, &ev
);
1420 if ( result
< (int)nBytes
) {
1421 errorString_
= "RtMidiOut::sendMessage: event parsing error!";
1422 error( RtError::WARNING
);
1427 result
= snd_seq_event_output(data
->seq
, &ev
);
1429 errorString_
= "RtMidiOut::sendMessage: error sending MIDI message to port.";
1430 error( RtError::WARNING
);
1432 snd_seq_drain_output(data
->seq
);
1435 #endif // __LINUX_ALSA__
1438 //*********************************************************************//
1440 //*********************************************************************//
1442 // API information gleamed from:
1443 // http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro
1445 // If the Makefile doesn't work, try the following:
1446 // CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd
1447 // CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd
1448 // CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd
1449 // CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd
1451 #if defined(__IRIX_MD__)
1453 #include <pthread.h>
1454 #include <sys/time.h>
1457 // Irix MIDI header file.
1458 #include <dmedia/midi.h>
1460 // A structure to hold variables related to the IRIX API
1462 struct IrixMidiData
{
1467 //*********************************************************************//
1469 // Class Definitions: RtMidiIn
1470 //*********************************************************************//
1472 extern "C" void *irixMidiHandler( void *ptr
)
1474 RtMidiIn::RtMidiInData
*data
= static_cast<RtMidiIn::RtMidiInData
*> (ptr
);
1475 IrixMidiData
*apiData
= static_cast<IrixMidiData
*> (data
->apiData
);
1477 bool continueSysex
= false;
1478 unsigned char status
;
1479 unsigned short size
;
1481 int fd
= mdGetFd( apiData
->port
);
1483 data
->doInput
= false;
1484 std::cerr
<< "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n";
1490 FD_SET( fd
, &mask
);
1491 struct timeval timeout
= {0, 0};
1492 RtMidiIn::MidiMessage message
;
1495 while ( data
->doInput
) {
1499 timeout
.tv_usec
= 0;
1500 if ( select( fd
+1, &rmask
, NULL
, NULL
, &timeout
) <= 0 ) {
1501 // No data pending ... sleep a bit.
1506 // If here, there should be data.
1507 result
= mdReceive( apiData
->port
, &event
, 1);
1508 if ( result
<= 0 ) {
1509 std::cerr
<< "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n";
1513 message
.timeStamp
= event
.stamp
* 0.000000001;
1516 status
= event
.msg
[0];
1517 if ( !(status
& 0x80) ) continue;
1518 if ( status
== 0xF0 ) {
1519 // Sysex message ... can be segmented across multiple messages.
1520 if ( !(data
->ignoreFlags
& 0x01) ) {
1521 if ( continueSysex
) {
1522 // We have a continuing, segmented sysex message. Append
1523 // the new bytes to our existing message.
1524 for ( int i
=0; i
<event
.msglen
; i
++ )
1525 message
.bytes
.push_back( event
.sysexmsg
[i
] );
1526 if ( event
.sysexmsg
[event
.msglen
-1] == 0xF7 ) continueSysex
= false;
1527 if ( !continueSysex
) {
1528 // If not a continuing sysex message, invoke the user callback function or queue the message.
1529 if ( data
->usingCallback
&& message
.bytes
.size() > 0 ) {
1530 RtMidiIn::RtMidiCallback callback
= (RtMidiIn::RtMidiCallback
) data
->userCallback
;
1531 callback( message
.timeStamp
, &message
.bytes
, data
->userData
);
1534 // As long as we haven't reached our queue size limit, push the message.
1535 if ( data
->queueLimit
> data
->queue
.size() )
1536 data
->queue
.push( message
);
1538 std::cerr
<< "\nRtMidiIn: message queue limit reached!!\n\n";
1540 message
.bytes
.clear();
1547 else if ( status
< 0xC0 ) size
= 3;
1548 else if ( status
< 0xE0 ) size
= 2;
1549 else if ( status
< 0xF0 ) size
= 3;
1550 else if ( status
< 0xF3 ) {
1551 if ( status
== 0xF1 && !(data
->ignoreFlags
& 0x02) ) {
1552 // A MIDI time code message and we're not ignoring it.
1556 else if ( status
== 0xF3 ) size
= 2;
1557 else if ( status
== 0xF8 ) {
1558 if ( !(data
->ignoreFlags
& 0x02) ) {
1559 // A MIDI timing tick message and we're not ignoring it.
1563 else if ( status
== 0xFE ) { // MIDI active sensing
1564 if ( !(data
->ignoreFlags
& 0x04) )
1569 // Copy the MIDI data to our vector.
1571 message
.bytes
.assign( &event
.msg
[0], &event
.msg
[size
] );
1572 // Invoke the user callback function or queue the message.
1573 if ( data
->usingCallback
) {
1574 RtMidiIn::RtMidiCallback callback
= (RtMidiIn::RtMidiCallback
) data
->userCallback
;
1575 callback( message
.timeStamp
, &message
.bytes
, data
->userData
);
1578 // As long as we haven't reached our queue size limit, push the message.
1579 if ( data
->queueLimit
> data
->queue
.size() )
1580 data
->queue
.push( message
);
1582 std::cerr
<< "\nRtMidiIn: message queue limit reached!!\n\n";
1584 message
.bytes
.clear();
1591 void RtMidiIn :: initialize( void )
1593 // Initialize the Irix MIDI system. At the moment, we will not
1594 // worry about a return value of zero (ports) because there is a
1595 // chance the user could plug something in after instantiation.
1596 int nPorts
= mdInit();
1598 // Create our api-specific connection information.
1599 IrixMidiData
*data
= (IrixMidiData
*) new IrixMidiData
;
1600 apiData_
= (void *) data
;
1601 inputData_
.apiData
= (void *) data
;
1604 void RtMidiIn :: openPort( unsigned int portNumber
)
1607 errorString_
= "RtMidiIn::openPort: a valid connection already exists!";
1608 error( RtError::WARNING
);
1612 int nPorts
= mdInit();
1614 errorString_
= "RtMidiIn::openPort: no Irix MIDI input sources found!";
1615 error( RtError::NO_DEVICES_FOUND
);
1618 std::ostringstream ost
;
1619 if ( portNumber
>= nPorts
) {
1620 ost
<< "RtMidiIn::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
1621 errorString_
= ost
.str();
1622 error( RtError::INVALID_PARAMETER
);
1625 IrixMidiData
*data
= static_cast<IrixMidiData
*> (apiData_
);
1626 data
->port
= mdOpenInPort( mdGetName(portNumber
) );
1627 if ( data
->port
== NULL
) {
1628 ost
<< "RtMidiIn::openPort: Irix error opening the port (" << portNumber
<< ").";
1629 errorString_
= ost
.str();
1630 error( RtError::DRIVER_ERROR
);
1632 mdSetStampMode(data
->port
, MD_DELTASTAMP
);
1634 // Start our MIDI input thread.
1635 pthread_attr_t attr
;
1636 pthread_attr_init(&attr
);
1637 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_JOINABLE
);
1638 pthread_attr_setschedpolicy(&attr
, SCHED_RR
);
1640 inputData_
.doInput
= true;
1641 int err
= pthread_create(&data
->thread
, &attr
, irixMidiHandler
, &inputData_
);
1642 pthread_attr_destroy(&attr
);
1644 mdClosePort( data
->port
);
1645 inputData_
.doInput
= false;
1646 errorString_
= "RtMidiIn::openPort: error starting MIDI input thread!";
1647 error( RtError::THREAD_ERROR
);
1653 void RtMidiIn :: openVirtualPort()
1655 // This function cannot be implemented for the Irix MIDI API.
1656 errorString_
= "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!";
1657 error( RtError::WARNING
);
1660 void RtMidiIn :: closePort( void )
1663 IrixMidiData
*data
= static_cast<IrixMidiData
*> (apiData_
);
1664 mdClosePort( data
->port
);
1667 // Shutdown the input thread.
1668 inputData_
.doInput
= false;
1669 pthread_join( data
->thread
, NULL
);
1673 RtMidiIn :: ~RtMidiIn()
1675 // Close a connection if it exists.
1679 IrixMidiData
*data
= static_cast<IrixMidiData
*> (apiData_
);
1683 unsigned int RtMidiIn :: getPortCount()
1685 int nPorts
= mdInit();
1686 if ( nPorts
>= 0 ) return nPorts
;
1690 std::string
RtMidiIn :: getPortName( unsigned int portNumber
)
1692 int nPorts
= mdInit();
1694 std::ostringstream ost
;
1695 if ( portNumber
>= nPorts
) {
1696 ost
<< "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber
<< ") is invalid.";
1697 errorString_
= ost
.str();
1698 error( RtError::INVALID_PARAMETER
);
1701 std::string stringName
= std::string( mdGetName( portNumber
) );
1705 //*********************************************************************//
1707 // Class Definitions: RtMidiOut
1708 //*********************************************************************//
1710 unsigned int RtMidiOut :: getPortCount()
1712 int nPorts
= mdInit();
1713 if ( nPorts
>= 0 ) return nPorts
;
1717 std::string
RtMidiOut :: getPortName( unsigned int portNumber
)
1719 int nPorts
= mdInit();
1721 std::ostringstream ost
;
1722 if ( portNumber
>= nPorts
) {
1723 ost
<< "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber
<< ") is invalid.";
1724 errorString_
= ost
.str();
1725 error( RtError::INVALID_PARAMETER
);
1728 std::string stringName
= std::string( mdGetName( portNumber
) );
1732 void RtMidiOut :: initialize( void )
1734 // Initialize the Irix MIDI system. At the moment, we will not
1735 // worry about a return value of zero (ports) because there is a
1736 // chance the user could plug something in after instantiation.
1737 int nPorts
= mdInit();
1739 // Create our api-specific connection information.
1740 IrixMidiData
*data
= (IrixMidiData
*) new IrixMidiData
;
1741 apiData_
= (void *) data
;
1744 void RtMidiOut :: openPort( unsigned int portNumber
)
1747 errorString_
= "RtMidiOut::openPort: a valid connection already exists!";
1748 error( RtError::WARNING
);
1752 int nPorts
= mdInit();
1754 errorString_
= "RtMidiOut::openPort: no Irix MIDI output sources found!";
1755 error( RtError::NO_DEVICES_FOUND
);
1758 std::ostringstream ost
;
1759 if ( portNumber
>= nPorts
) {
1760 ost
<< "RtMidiOut::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
1761 errorString_
= ost
.str();
1762 error( RtError::INVALID_PARAMETER
);
1765 IrixMidiData
*data
= static_cast<IrixMidiData
*> (apiData_
);
1766 data
->port
= mdOpenOutPort( mdGetName(portNumber
) );
1767 if ( data
->port
== NULL
) {
1768 ost
<< "RtMidiOut::openPort: Irix error opening the port (" << portNumber
<< ").";
1769 errorString_
= ost
.str();
1770 error( RtError::DRIVER_ERROR
);
1772 mdSetStampMode(data
->port
, MD_NOSTAMP
);
1777 void RtMidiOut :: closePort( void )
1780 IrixMidiData
*data
= static_cast<IrixMidiData
*> (apiData_
);
1781 mdClosePort( data
->port
);
1786 void RtMidiOut :: openVirtualPort()
1788 // This function cannot be implemented for the Irix MIDI API.
1789 errorString_
= "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!";
1790 error( RtError::WARNING
);
1793 RtMidiOut :: ~RtMidiOut()
1795 // Close a connection if it exists.
1799 IrixMidiData
*data
= static_cast<IrixMidiData
*> (apiData_
);
1803 void RtMidiOut :: sendMessage( std::vector
<unsigned char> *message
)
1807 IrixMidiData
*data
= static_cast<IrixMidiData
*> (apiData_
);
1810 unsigned int nBytes
= message
->size();
1811 if ( nBytes
== 0 ) return;
1813 if ( message
->at(0) == 0xF0 ) {
1814 if ( nBytes
< 3 ) return; // check for bogus sysex
1815 event
.msg
[0] = 0xF0;
1816 event
.msglen
= nBytes
;
1817 buffer
= (char *) malloc( nBytes
);
1818 for ( int i
=0; i
<nBytes
; i
++ ) buffer
[i
] = message
->at(i
);
1819 event
.sysexmsg
= buffer
;
1822 for ( int i
=0; i
<nBytes
; i
++ )
1823 event
.msg
[i
] = message
->at(i
);
1827 result
= mdSend( data
->port
, &event
, 1 );
1828 if ( buffer
) free( buffer
);
1830 errorString_
= "RtMidiOut::sendMessage: IRIX error sending MIDI message!";
1831 error( RtError::WARNING
);
1836 #endif // __IRIX_MD__
1838 //*********************************************************************//
1839 // API: Windows Multimedia Library (MM)
1840 //*********************************************************************//
1842 // API information deciphered from:
1843 // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
1846 #if defined(__WINDOWS_DS__)
1847 //#if defined(__WINDOWS_MM__)
1849 // The Windows MM API is based on the use of a callback function for
1850 // MIDI input. We convert the system specific time stamps to delta
1853 // Windows MM MIDI header files.
1854 #include <windows.h>
1855 #include <mmsystem.h>
1857 // A structure to hold variables related to the CoreMIDI API
1859 struct WinMidiData
{
1860 HMIDIIN inHandle
; // Handle to Midi Input Device
1861 HMIDIOUT outHandle
; // Handle to Midi Output Device
1863 RtMidiIn::MidiMessage message
;
1866 //*********************************************************************//
1868 // Class Definitions: RtMidiIn
1869 //*********************************************************************//
1871 static void CALLBACK
midiInputCallback( HMIDIOUT hmin
,
1877 if ( inputStatus
!= MIM_DATA
&& inputStatus
!= MIM_LONGDATA
) return;
1879 //RtMidiIn::RtMidiInData *data = static_cast<RtMidiIn::RtMidiInData *> (instancePtr);
1880 RtMidiIn::RtMidiInData
*data
= (RtMidiIn::RtMidiInData
*)instancePtr
;
1881 WinMidiData
*apiData
= static_cast<WinMidiData
*> (data
->apiData
);
1883 // Calculate time stamp.
1884 apiData
->message
.timeStamp
= 0.0;
1885 if ( data
->firstMessage
== true ) data
->firstMessage
= false;
1886 else apiData
->message
.timeStamp
= (double) ( timestamp
- apiData
->lastTime
) * 0.001;
1887 apiData
->lastTime
= timestamp
;
1889 if ( inputStatus
== MIM_DATA
) { // Channel or system message
1891 // Make sure the first byte is a status byte.
1892 unsigned char status
= (unsigned char) (midiMessage
& 0x000000FF);
1893 if ( !(status
& 0x80) ) return;
1895 // Determine the number of bytes in the MIDI message.
1896 unsigned short nBytes
= 1;
1897 if ( status
< 0xC0 ) nBytes
= 3;
1898 else if ( status
< 0xE0 ) nBytes
= 2;
1899 else if ( status
< 0xF0 ) nBytes
= 3;
1900 else if ( status
< 0xF3 ) {
1901 // A MIDI time code message and we're ignoring it.
1902 if ( status
== 0xF1 && (data
->ignoreFlags
& 0x02) ) return;
1905 else if ( status
== 0xF3 ) nBytes
= 2;
1906 else if ( status
== 0xF8 && (data
->ignoreFlags
& 0x02) ) {
1907 // A MIDI timing tick message and we're ignoring it.
1910 else if ( status
== 0xFE && (data
->ignoreFlags
& 0x04) ) {
1911 // A MIDI active sensing message and we're ignoring it.
1915 // Copy bytes to our MIDI message.
1916 unsigned char *ptr
= (unsigned char *) &midiMessage
;
1917 for ( int i
=0; i
<nBytes
; i
++ ) apiData
->message
.bytes
.push_back( *ptr
++ );
1919 else { // Sysex message
1920 MIDIHDR
*sysex
= ( MIDIHDR
*) midiMessage
;
1921 for ( int i
=0; i
<(int)sysex
->dwBytesRecorded
; i
++ )
1922 apiData
->message
.bytes
.push_back( sysex
->lpData
[i
] );
1923 if ( apiData
->message
.bytes
.back() != 0xF7 ) return;
1926 if ( data
->usingCallback
) {
1927 RtMidiIn::RtMidiCallback callback
= (RtMidiIn::RtMidiCallback
) data
->userCallback
;
1928 callback( apiData
->message
.timeStamp
, &apiData
->message
.bytes
, data
->userData
);
1931 // As long as we haven't reached our queue size limit, push the message.
1932 if ( data
->queueLimit
> data
->queue
.size() )
1933 data
->queue
.push( apiData
->message
);
1935 std::cerr
<< "\nRtMidiIn: message queue limit reached!!\n\n";
1938 // Clear the vector for the next input message. Note that doing
1939 // this here allows our code to work for sysex messages which are
1940 // segmented across multiple buffers.
1941 apiData
->message
.bytes
.clear();
1944 void RtMidiIn :: initialize( void )
1946 // We'll issue a warning here if no devices are available but not
1947 // throw an error since the user can plugin something later.
1948 unsigned int nDevices
= midiInGetNumDevs();
1949 if ( nDevices
== 0 ) {
1950 errorString_
= "RtMidiIn::initialize: no MIDI input devices currently available.";
1951 error( RtError::WARNING
);
1954 // Save our api-specific connection information.
1955 WinMidiData
*data
= (WinMidiData
*) new WinMidiData
;
1956 apiData_
= (void *) data
;
1957 inputData_
.apiData
= (void *) data
;
1958 data
->message
.bytes
.clear(); // needs to be empty for first input message
1961 void RtMidiIn :: openPort( unsigned int portNumber
)
1964 errorString_
= "RtMidiIn::openPort: a valid connection already exists!";
1965 error( RtError::WARNING
);
1969 unsigned int nDevices
= midiInGetNumDevs();
1970 if (nDevices
== 0) {
1971 errorString_
= "RtMidiIn::openPort: no MIDI input sources found!";
1972 error( RtError::NO_DEVICES_FOUND
);
1975 std::ostringstream ost
;
1976 if ( portNumber
>= nDevices
) {
1977 ost
<< "RtMidiIn::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
1978 errorString_
= ost
.str();
1979 error( RtError::INVALID_PARAMETER
);
1982 WinMidiData
*data
= static_cast<WinMidiData
*> (apiData_
);
1983 MMRESULT result
= midiInOpen( &data
->inHandle
,
1985 (DWORD
)&midiInputCallback
,
1987 CALLBACK_FUNCTION
);
1988 if ( result
!= MMSYSERR_NOERROR
) {
1989 errorString_
= "RtMidiIn::openPort: error creating Windows MM MIDI input port.";
1990 error( RtError::DRIVER_ERROR
);
1993 result
= midiInStart( data
->inHandle
);
1994 if ( result
!= MMSYSERR_NOERROR
) {
1995 midiInClose( data
->inHandle
);
1996 errorString_
= "RtMidiIn::openPort: error starting Windows MM MIDI input port.";
1997 error( RtError::DRIVER_ERROR
);
2003 void RtMidiIn :: openVirtualPort()
2005 // This function cannot be implemented for the Windows MM MIDI API.
2006 errorString_
= "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
2007 error( RtError::WARNING
);
2010 void RtMidiIn :: closePort( void )
2013 WinMidiData
*data
= static_cast<WinMidiData
*> (apiData_
);
2014 midiInReset( data
->inHandle
);
2015 midiInStop( data
->inHandle
);
2016 midiInClose( data
->inHandle
);
2021 RtMidiIn :: ~RtMidiIn()
2023 // Close a connection if it exists.
2027 WinMidiData
*data
= static_cast<WinMidiData
*> (apiData_
);
2031 unsigned int RtMidiIn :: getPortCount()
2033 return midiInGetNumDevs();
2036 std::string
RtMidiIn :: getPortName( unsigned int portNumber
)
2038 unsigned int nDevices
= midiInGetNumDevs();
2039 if ( portNumber
>= nDevices
) {
2040 std::ostringstream ost
;
2041 ost
<< "RtMidiIn::getPortName: 'portNumber' argument (" << portNumber
<< ") is invalid.";
2042 errorString_
= ost
.str();
2043 error( RtError::INVALID_PARAMETER
);
2046 MIDIINCAPS deviceCaps
;
2047 MMRESULT result
= midiInGetDevCaps( portNumber
, &deviceCaps
, sizeof(MIDIINCAPS
));
2049 std::string stringName
= std::string( deviceCaps
.szPname
);
2053 //*********************************************************************//
2055 // Class Definitions: RtMidiOut
2056 //*********************************************************************//
2058 unsigned int RtMidiOut :: getPortCount()
2060 return midiOutGetNumDevs();
2063 std::string
RtMidiOut :: getPortName( unsigned int portNumber
)
2065 unsigned int nDevices
= midiOutGetNumDevs();
2066 if ( portNumber
>= nDevices
) {
2067 std::ostringstream ost
;
2068 ost
<< "RtMidiOut::getPortName: 'portNumber' argument (" << portNumber
<< ") is invalid.";
2069 errorString_
= ost
.str();
2070 error( RtError::INVALID_PARAMETER
);
2073 MIDIOUTCAPS deviceCaps
;
2074 MMRESULT result
= midiOutGetDevCaps( portNumber
, &deviceCaps
, sizeof(MIDIOUTCAPS
));
2076 std::string stringName
= std::string( deviceCaps
.szPname
);
2080 void RtMidiOut :: initialize( void )
2082 // We'll issue a warning here if no devices are available but not
2083 // throw an error since the user can plug something in later.
2084 unsigned int nDevices
= midiOutGetNumDevs();
2085 if ( nDevices
== 0 ) {
2086 errorString_
= "RtMidiOut::initialize: no MIDI output devices currently available.";
2087 error( RtError::WARNING
);
2090 // Save our api-specific connection information.
2091 WinMidiData
*data
= (WinMidiData
*) new WinMidiData
;
2092 apiData_
= (void *) data
;
2095 void RtMidiOut :: openPort( unsigned int portNumber
)
2098 errorString_
= "RtMidiOut::openPort: a valid connection already exists!";
2099 error( RtError::WARNING
);
2103 unsigned int nDevices
= midiOutGetNumDevs();
2105 errorString_
= "RtMidiOut::openPort: no MIDI output destinations found!";
2106 error( RtError::NO_DEVICES_FOUND
);
2109 std::ostringstream ost
;
2110 if ( portNumber
>= nDevices
) {
2111 ost
<< "RtMidiOut::openPort: 'portNumber' argument (" << portNumber
<< ") is invalid.";
2112 errorString_
= ost
.str();
2113 error( RtError::INVALID_PARAMETER
);
2116 WinMidiData
*data
= static_cast<WinMidiData
*> (apiData_
);
2117 MMRESULT result
= midiOutOpen( &data
->outHandle
,
2122 if ( result
!= MMSYSERR_NOERROR
) {
2123 errorString_
= "RtMidiOut::openPort: error creating Windows MM MIDI output port.";
2124 error( RtError::DRIVER_ERROR
);
2130 void RtMidiOut :: closePort( void )
2133 WinMidiData
*data
= static_cast<WinMidiData
*> (apiData_
);
2134 midiOutReset( data
->outHandle
);
2135 midiOutClose( data
->outHandle
);
2140 void RtMidiOut :: openVirtualPort()
2142 // This function cannot be implemented for the Windows MM MIDI API.
2143 errorString_
= "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
2144 error( RtError::WARNING
);
2147 RtMidiOut :: ~RtMidiOut()
2149 // Close a connection if it exists.
2153 WinMidiData
*data
= static_cast<WinMidiData
*> (apiData_
);
2157 void RtMidiOut :: sendMessage( std::vector
<unsigned char> *message
)
2159 unsigned int nBytes
= message
->size();
2160 if ( nBytes
== 0 ) {
2161 errorString_
= "RtMidiOut::sendMessage: message argument is empty!";
2162 error( RtError::WARNING
);
2167 WinMidiData
*data
= static_cast<WinMidiData
*> (apiData_
);
2168 if ( message
->at(0) == 0xF0 ) { // Sysex message
2170 // Allocate buffer for sysex data.
2171 char *buffer
= (char *) malloc( nBytes
);
2172 if ( buffer
== NULL
) {
2173 errorString_
= "RtMidiOut::sendMessage: error allocating sysex message memory!";
2174 error( RtError::MEMORY_ERROR
);
2177 // Copy data to buffer.
2178 for ( unsigned int i
=0; i
<nBytes
; i
++ ) buffer
[i
] = message
->at(i
);
2180 // Create and prepare MIDIHDR structure.
2182 sysex
.lpData
= (LPSTR
) buffer
;
2183 sysex
.dwBufferLength
= nBytes
;
2185 result
= midiOutPrepareHeader( data
->outHandle
, &sysex
, sizeof(MIDIHDR
) );
2186 if ( result
!= MMSYSERR_NOERROR
) {
2188 errorString_
= "RtMidiOut::sendMessage: error preparing sysex header.";
2189 error( RtError::DRIVER_ERROR
);
2192 // Send the message.
2193 result
= midiOutLongMsg( data
->outHandle
, &sysex
, sizeof(MIDIHDR
) );
2194 if ( result
!= MMSYSERR_NOERROR
) {
2196 errorString_
= "RtMidiOut::sendMessage: error sending sysex message.";
2197 error( RtError::DRIVER_ERROR
);
2200 // Unprepare the buffer and MIDIHDR.
2201 while ( MIDIERR_STILLPLAYING
== midiOutUnprepareHeader( data
->outHandle
, &sysex
, sizeof (MIDIHDR
) ) ) Sleep( 1 );
2205 else { // Channel or system message.
2207 // Make sure the message size isn't too big.
2209 errorString_
= "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!";
2210 error( RtError::WARNING
);
2214 // Pack MIDI bytes into double word.
2216 unsigned char *ptr
= (unsigned char *) &packet
;
2217 for ( unsigned int i
=0; i
<nBytes
; i
++ ) {
2218 *ptr
= message
->at(i
);
2222 // Send the message immediately.
2223 result
= midiOutShortMsg( data
->outHandle
, packet
);
2224 if ( result
!= MMSYSERR_NOERROR
) {
2225 errorString_
= "RtMidiOut::sendMessage: error sending MIDI message.";
2226 error( RtError::DRIVER_ERROR
);
2231 #endif // __WINDOWS_MM__
2234 #if defined(__LINUX_OSS__) // dummy
2236 /*void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef )
2238 fprintf( stderr, "[chuck](via rtmidi): midi not supported for this platform\n" );
2241 void RtMidiIn :: initialize( void )
2243 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2246 void RtMidiIn :: openPort( unsigned int portNumber
)
2248 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2251 void RtMidiIn :: openVirtualPort()
2253 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2256 void RtMidiIn :: closePort( void )
2258 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2261 RtMidiIn :: ~RtMidiIn()
2263 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2266 unsigned int RtMidiIn :: getPortCount()
2271 std::string
RtMidiIn :: getPortName( unsigned int portNumber
)
2273 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2277 //*********************************************************************//
2279 // Class Definitions: RtMidiOut
2280 //*********************************************************************//
2282 unsigned int RtMidiOut :: getPortCount()
2287 std::string
RtMidiOut :: getPortName( unsigned int portNumber
)
2289 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2293 void RtMidiOut :: initialize( void )
2295 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2298 void RtMidiOut :: openPort( unsigned int portNumber
)
2300 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2303 void RtMidiOut :: closePort( void )
2305 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2308 void RtMidiOut :: openVirtualPort()
2310 fprintf( stderr
, "[chuck](via rtmidi): midi not supported for this platform\n" );
2313 RtMidiOut :: ~RtMidiOut()
2317 void RtMidiOut :: sendMessage( std::vector
<unsigned char> *message
)