comparison data/midi/rtmidi/RtMidi.cpp @ 559:32d156c75df7

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