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