comparison DPF-Prymula-audioplugins/dpf/distrho/src/jackbridge/rtmidi/RtMidi.cpp @ 3:84e66ea83026

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