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__