2022-02-13 20:02:43 +00:00
/**********************************************************************/
/*! \class RtMidi
\ brief An abstract base class for realtime MIDI input / output .
This class implements some common functionality for the realtime
MIDI input / output subclasses RtMidiIn and RtMidiOut .
RtMidi GitHub site : https : //github.com/thestk/rtmidi
RtMidi WWW site : http : //www.music.mcgill.ca/~gary/rtmidi/
RtMidi : realtime MIDI i / o C + + classes
Copyright ( c ) 2003 - 2021 Gary P . Scavone
Permission is hereby granted , free of charge , to any person
obtaining a copy of this software and associated documentation files
( the " Software " ) , to deal in the Software without restriction ,
including without limitation the rights to use , copy , modify , merge ,
publish , distribute , sublicense , and / or sell copies of the Software ,
and to permit persons to whom the Software is furnished to do so ,
subject to the following conditions :
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software .
Any person wishing to distribute modifications to the Software is
asked to send the modifications to the original developer so that
they can be incorporated into the canonical version . This is ,
however , not a binding provision of this license .
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF
CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
/**********************************************************************/
# include "RtMidi.h"
# include <sstream>
2022-06-30 08:44:03 +00:00
# ifdef TARGET_OS_IPHONE
2022-02-13 20:02:43 +00:00
# if (TARGET_OS_IPHONE == 1)
# define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
# define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
# include <mach/mach_time.h>
class CTime2nsFactor
{
public :
CTime2nsFactor ( )
{
mach_timebase_info_data_t tinfo ;
mach_timebase_info ( & tinfo ) ;
Factor = ( double ) tinfo . numer / tinfo . denom ;
}
static double Factor ;
} ;
double CTime2nsFactor : : Factor ;
static CTime2nsFactor InitTime2nsFactor ;
# undef AudioGetCurrentHostTime
# undef AudioConvertHostTimeToNanos
# define AudioGetCurrentHostTime (uint64_t) mach_absolute_time
# define AudioConvertHostTimeToNanos(t) t *CTime2nsFactor::Factor
# define EndianS32_BtoN(n) n
# endif
2022-06-30 08:44:03 +00:00
# endif
2022-02-13 20:02:43 +00:00
// Default for Windows is to add an identifier to the port names; this
// flag can be defined (e.g. in your project file) to disable this behaviour.
//#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
// **************************************************************** //
//
// MidiInApi and MidiOutApi subclass prototypes.
//
// **************************************************************** //
# if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(TARGET_IPHONE_OS) && !defined(__WEB_MIDI_API__)
# define __RTMIDI_DUMMY__
# endif
# if defined(__MACOSX_CORE__)
# include <CoreMIDI/CoreMIDI.h>
class MidiInCore : public MidiInApi
{
public :
MidiInCore ( const std : : string & clientName , unsigned int queueSizeLimit ) ;
~ MidiInCore ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : MACOSX_CORE ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
protected :
MIDIClientRef getCoreMidiClientSingleton ( const std : : string & clientName ) throw ( ) ;
void initialize ( const std : : string & clientName ) ;
} ;
class MidiOutCore : public MidiOutApi
{
public :
MidiOutCore ( const std : : string & clientName ) ;
~ MidiOutCore ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : MACOSX_CORE ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
void sendMessage ( const unsigned char * message , size_t size ) ;
protected :
MIDIClientRef getCoreMidiClientSingleton ( const std : : string & clientName ) throw ( ) ;
void initialize ( const std : : string & clientName ) ;
} ;
# endif
# if defined(__UNIX_JACK__)
class MidiInJack : public MidiInApi
{
public :
MidiInJack ( const std : : string & clientName , unsigned int queueSizeLimit ) ;
~ MidiInJack ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : UNIX_JACK ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
protected :
std : : string clientName ;
void connect ( void ) ;
void initialize ( const std : : string & clientName ) ;
} ;
class MidiOutJack : public MidiOutApi
{
public :
MidiOutJack ( const std : : string & clientName ) ;
~ MidiOutJack ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : UNIX_JACK ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
void sendMessage ( const unsigned char * message , size_t size ) ;
protected :
std : : string clientName ;
void connect ( void ) ;
void initialize ( const std : : string & clientName ) ;
} ;
# endif
# if defined(__LINUX_ALSA__)
class MidiInAlsa : public MidiInApi
{
public :
MidiInAlsa ( const std : : string & clientName , unsigned int queueSizeLimit ) ;
~ MidiInAlsa ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : LINUX_ALSA ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
protected :
void initialize ( const std : : string & clientName ) ;
} ;
class MidiOutAlsa : public MidiOutApi
{
public :
MidiOutAlsa ( const std : : string & clientName ) ;
~ MidiOutAlsa ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : LINUX_ALSA ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
void sendMessage ( const unsigned char * message , size_t size ) ;
protected :
void initialize ( const std : : string & clientName ) ;
} ;
# endif
# if defined(__WINDOWS_MM__)
class MidiInWinMM : public MidiInApi
{
public :
MidiInWinMM ( const std : : string & clientName , unsigned int queueSizeLimit ) ;
~ MidiInWinMM ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : WINDOWS_MM ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
protected :
void initialize ( const std : : string & clientName ) ;
} ;
class MidiOutWinMM : public MidiOutApi
{
public :
MidiOutWinMM ( const std : : string & clientName ) ;
~ MidiOutWinMM ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : WINDOWS_MM ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
void sendMessage ( const unsigned char * message , size_t size ) ;
protected :
void initialize ( const std : : string & clientName ) ;
} ;
# endif
# if defined(__WEB_MIDI_API__)
class MidiInWeb : public MidiInApi
{
std : : string client_name { } ;
std : : string web_midi_id { } ;
int open_port_number { - 1 } ;
public :
MidiInWeb ( const std : : string & /*clientName*/ , unsigned int queueSizeLimit ) ;
~ MidiInWeb ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : WEB_MIDI_API ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
void onMidiMessage ( uint8_t * data , double domHishResTimeStamp ) ;
protected :
void initialize ( const std : : string & clientName ) ;
} ;
class MidiOutWeb : public MidiOutApi
{
std : : string client_name { } ;
std : : string web_midi_id { } ;
int open_port_number { - 1 } ;
public :
MidiOutWeb ( const std : : string & clientName ) ;
~ MidiOutWeb ( void ) ;
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : WEB_MIDI_API ; } ;
void openPort ( unsigned int portNumber , const std : : string & portName ) ;
void openVirtualPort ( const std : : string & portName ) ;
void closePort ( void ) ;
void setClientName ( const std : : string & clientName ) ;
void setPortName ( const std : : string & portName ) ;
unsigned int getPortCount ( void ) ;
std : : string getPortName ( unsigned int portNumber ) ;
void sendMessage ( const unsigned char * message , size_t size ) ;
protected :
void initialize ( const std : : string & clientName ) ;
} ;
# endif
# if defined(__RTMIDI_DUMMY__)
class MidiInDummy : public MidiInApi
{
public :
MidiInDummy ( const std : : string & /*clientName*/ , unsigned int queueSizeLimit ) : MidiInApi ( queueSizeLimit ) { errorString_ = " MidiInDummy: This class provides no functionality. " ; error ( RtMidiError : : WARNING , errorString_ ) ; }
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : RTMIDI_DUMMY ; }
void openPort ( unsigned int /*portNumber*/ , const std : : string & /*portName*/ ) { }
void openVirtualPort ( const std : : string & /*portName*/ ) { }
void closePort ( void ) { }
void setClientName ( const std : : string & /*clientName*/ ) { } ;
void setPortName ( const std : : string & /*portName*/ ) { } ;
unsigned int getPortCount ( void ) { return 0 ; }
std : : string getPortName ( unsigned int /*portNumber*/ ) { return " " ; }
protected :
void initialize ( const std : : string & /*clientName*/ ) { }
} ;
class MidiOutDummy : public MidiOutApi
{
public :
MidiOutDummy ( const std : : string & /*clientName*/ ) { errorString_ = " MidiOutDummy: This class provides no functionality. " ; error ( RtMidiError : : WARNING , errorString_ ) ; }
RtMidi : : Api getCurrentApi ( void ) { return RtMidi : : RTMIDI_DUMMY ; }
void openPort ( unsigned int /*portNumber*/ , const std : : string & /*portName*/ ) { }
void openVirtualPort ( const std : : string & /*portName*/ ) { }
void closePort ( void ) { }
void setClientName ( const std : : string & /*clientName*/ ) { } ;
void setPortName ( const std : : string & /*portName*/ ) { } ;
unsigned int getPortCount ( void ) { return 0 ; }
std : : string getPortName ( unsigned int /*portNumber*/ ) { return " " ; }
void sendMessage ( const unsigned char * /*message*/ , size_t /*size*/ ) { }
protected :
void initialize ( const std : : string & /*clientName*/ ) { }
} ;
# endif
//*********************************************************************//
// RtMidi Definitions
//*********************************************************************//
RtMidi : : RtMidi ( )
: rtapi_ ( 0 )
{
}
RtMidi : : ~ RtMidi ( )
{
delete rtapi_ ;
rtapi_ = 0 ;
}
RtMidi : : RtMidi ( RtMidi & & other ) noexcept {
rtapi_ = other . rtapi_ ;
other . rtapi_ = nullptr ;
}
std : : string RtMidi : : getVersion ( void ) throw ( )
{
return std : : string ( RTMIDI_VERSION ) ;
}
// Define API names and display names.
// Must be in same order as API enum.
extern " C " {
const char * rtmidi_api_names [ ] [ 2 ] = {
{ " unspecified " , " Unknown " } ,
{ " core " , " CoreMidi " } ,
{ " alsa " , " ALSA " } ,
{ " jack " , " Jack " } ,
{ " winmm " , " Windows MultiMedia " } ,
{ " web " , " Web MIDI API " } ,
{ " dummy " , " Dummy " } ,
} ;
const unsigned int rtmidi_num_api_names =
sizeof ( rtmidi_api_names ) / sizeof ( rtmidi_api_names [ 0 ] ) ;
// The order here will control the order of RtMidi's API search in
// the constructor.
extern " C " const RtMidi : : Api rtmidi_compiled_apis [ ] = {
# if defined(__MACOSX_CORE__)
RtMidi : : MACOSX_CORE ,
# endif
# if defined(__LINUX_ALSA__)
RtMidi : : LINUX_ALSA ,
# endif
# if defined(__UNIX_JACK__)
RtMidi : : UNIX_JACK ,
# endif
# if defined(__WINDOWS_MM__)
RtMidi : : WINDOWS_MM ,
# endif
# if defined(__WEB_MIDI_API__)
RtMidi : : WEB_MIDI_API ,
# endif
# if defined(__RTMIDI_DUMMY__)
RtMidi : : RTMIDI_DUMMY ,
# endif
RtMidi : : UNSPECIFIED ,
} ;
extern " C " const unsigned int rtmidi_num_compiled_apis =
sizeof ( rtmidi_compiled_apis ) / sizeof ( rtmidi_compiled_apis [ 0 ] ) - 1 ;
}
// This is a compile-time check that rtmidi_num_api_names == RtMidi::NUM_APIS.
// If the build breaks here, check that they match.
template < bool b > class StaticAssert { private : StaticAssert ( ) { } } ;
template < > class StaticAssert < true > { public : StaticAssert ( ) { } } ;
class StaticAssertions { StaticAssertions ( ) {
StaticAssert < rtmidi_num_api_names = = RtMidi : : NUM_APIS > ( ) ;
} } ;
void RtMidi : : getCompiledApi ( std : : vector < RtMidi : : Api > & apis ) throw ( )
{
apis = std : : vector < RtMidi : : Api > ( rtmidi_compiled_apis ,
rtmidi_compiled_apis + rtmidi_num_compiled_apis ) ;
}
std : : string RtMidi : : getApiName ( RtMidi : : Api api )
{
if ( api < RtMidi : : UNSPECIFIED | | api > = RtMidi : : NUM_APIS )
return " " ;
return rtmidi_api_names [ api ] [ 0 ] ;
}
std : : string RtMidi : : getApiDisplayName ( RtMidi : : Api api )
{
if ( api < RtMidi : : UNSPECIFIED | | api > = RtMidi : : NUM_APIS )
return " Unknown " ;
return rtmidi_api_names [ api ] [ 1 ] ;
}
RtMidi : : Api RtMidi : : getCompiledApiByName ( const std : : string & name )
{
unsigned int i = 0 ;
for ( i = 0 ; i < rtmidi_num_compiled_apis ; + + i )
if ( name = = rtmidi_api_names [ rtmidi_compiled_apis [ i ] ] [ 0 ] )
return rtmidi_compiled_apis [ i ] ;
return RtMidi : : UNSPECIFIED ;
}
void RtMidi : : setClientName ( const std : : string & clientName )
{
rtapi_ - > setClientName ( clientName ) ;
}
void RtMidi : : setPortName ( const std : : string & portName )
{
rtapi_ - > setPortName ( portName ) ;
}
//*********************************************************************//
// RtMidiIn Definitions
//*********************************************************************//
void RtMidiIn : : openMidiApi ( RtMidi : : Api api , const std : : string & clientName , unsigned int queueSizeLimit )
{
delete rtapi_ ;
rtapi_ = 0 ;
# if defined(__UNIX_JACK__)
if ( api = = UNIX_JACK )
rtapi_ = new MidiInJack ( clientName , queueSizeLimit ) ;
# endif
# if defined(__LINUX_ALSA__)
if ( api = = LINUX_ALSA )
rtapi_ = new MidiInAlsa ( clientName , queueSizeLimit ) ;
# endif
# if defined(__WINDOWS_MM__)
if ( api = = WINDOWS_MM )
rtapi_ = new MidiInWinMM ( clientName , queueSizeLimit ) ;
# endif
# if defined(__MACOSX_CORE__)
if ( api = = MACOSX_CORE )
rtapi_ = new MidiInCore ( clientName , queueSizeLimit ) ;
# endif
# if defined(__WEB_MIDI_API__)
if ( api = = WEB_MIDI_API )
rtapi_ = new MidiInWeb ( clientName , queueSizeLimit ) ;
# endif
# if defined(__RTMIDI_DUMMY__)
if ( api = = RTMIDI_DUMMY )
rtapi_ = new MidiInDummy ( clientName , queueSizeLimit ) ;
# endif
}
RTMIDI_DLL_PUBLIC RtMidiIn : : RtMidiIn ( RtMidi : : Api api , const std : : string & clientName , unsigned int queueSizeLimit )
: RtMidi ( )
{
if ( api ! = UNSPECIFIED ) {
// Attempt to open the specified API.
openMidiApi ( api , clientName , queueSizeLimit ) ;
if ( rtapi_ ) return ;
// No compiled support for specified API value. Issue a warning
// and continue as if no API was specified.
std : : cerr < < " \n RtMidiIn: no compiled support for specified API argument! \n \n " < < std : : endl ;
}
// Iterate through the compiled APIs and return as soon as we find
// one with at least one port or we reach the end of the list.
std : : vector < RtMidi : : Api > apis ;
getCompiledApi ( apis ) ;
for ( unsigned int i = 0 ; i < apis . size ( ) ; i + + ) {
openMidiApi ( apis [ i ] , clientName , queueSizeLimit ) ;
if ( rtapi_ & & rtapi_ - > getPortCount ( ) ) break ;
}
if ( rtapi_ ) return ;
// It should not be possible to get here because the preprocessor
// definition __RTMIDI_DUMMY__ is automatically defined if no
// API-specific definitions are passed to the compiler. But just in
// case something weird happens, we'll throw an error.
std : : string errorText = " RtMidiIn: no compiled API support found ... critical error!! " ;
throw ( RtMidiError ( errorText , RtMidiError : : UNSPECIFIED ) ) ;
}
RtMidiIn : : ~ RtMidiIn ( ) throw ( )
{
}
//*********************************************************************//
// RtMidiOut Definitions
//*********************************************************************//
void RtMidiOut : : openMidiApi ( RtMidi : : Api api , const std : : string & clientName )
{
delete rtapi_ ;
rtapi_ = 0 ;
# if defined(__UNIX_JACK__)
if ( api = = UNIX_JACK )
rtapi_ = new MidiOutJack ( clientName ) ;
# endif
# if defined(__LINUX_ALSA__)
if ( api = = LINUX_ALSA )
rtapi_ = new MidiOutAlsa ( clientName ) ;
# endif
# if defined(__WINDOWS_MM__)
if ( api = = WINDOWS_MM )
rtapi_ = new MidiOutWinMM ( clientName ) ;
# endif
# if defined(__MACOSX_CORE__)
if ( api = = MACOSX_CORE )
rtapi_ = new MidiOutCore ( clientName ) ;
# endif
# if defined(__WEB_MIDI_API__)
if ( api = = WEB_MIDI_API )
rtapi_ = new MidiOutWeb ( clientName ) ;
# endif
# if defined(__RTMIDI_DUMMY__)
if ( api = = RTMIDI_DUMMY )
rtapi_ = new MidiOutDummy ( clientName ) ;
# endif
}
RTMIDI_DLL_PUBLIC RtMidiOut : : RtMidiOut ( RtMidi : : Api api , const std : : string & clientName )
{
if ( api ! = UNSPECIFIED ) {
// Attempt to open the specified API.
openMidiApi ( api , clientName ) ;
if ( rtapi_ ) return ;
// No compiled support for specified API value. Issue a warning
// and continue as if no API was specified.
std : : cerr < < " \n RtMidiOut: no compiled support for specified API argument! \n \n " < < std : : endl ;
}
// Iterate through the compiled APIs and return as soon as we find
// one with at least one port or we reach the end of the list.
std : : vector < RtMidi : : Api > apis ;
getCompiledApi ( apis ) ;
for ( unsigned int i = 0 ; i < apis . size ( ) ; i + + ) {
openMidiApi ( apis [ i ] , clientName ) ;
if ( rtapi_ & & rtapi_ - > getPortCount ( ) ) break ;
}
if ( rtapi_ ) return ;
// It should not be possible to get here because the preprocessor
// definition __RTMIDI_DUMMY__ is automatically defined if no
// API-specific definitions are passed to the compiler. But just in
// case something weird happens, we'll thrown an error.
std : : string errorText = " RtMidiOut: no compiled API support found ... critical error!! " ;
throw ( RtMidiError ( errorText , RtMidiError : : UNSPECIFIED ) ) ;
}
RtMidiOut : : ~ RtMidiOut ( ) throw ( )
{
}
//*********************************************************************//
// Common MidiApi Definitions
//*********************************************************************//
MidiApi : : MidiApi ( void )
: apiData_ ( 0 ) , connected_ ( false ) , errorCallback_ ( 0 ) , firstErrorOccurred_ ( false ) , errorCallbackUserData_ ( 0 )
{
}
MidiApi : : ~ MidiApi ( void )
{
}
void MidiApi : : setErrorCallback ( RtMidiErrorCallback errorCallback , void * userData = 0 )
{
errorCallback_ = errorCallback ;
errorCallbackUserData_ = userData ;
}
void MidiApi : : error ( RtMidiError : : Type type , std : : string errorString )
{
if ( errorCallback_ ) {
if ( firstErrorOccurred_ )
return ;
firstErrorOccurred_ = true ;
const std : : string errorMessage = errorString ;
errorCallback_ ( type , errorMessage , errorCallbackUserData_ ) ;
firstErrorOccurred_ = false ;
return ;
}
if ( type = = RtMidiError : : WARNING ) {
std : : cerr < < ' \n ' < < errorString < < " \n \n " ;
}
else if ( type = = RtMidiError : : DEBUG_WARNING ) {
# if defined(__RTMIDI_DEBUG__)
std : : cerr < < ' \n ' < < errorString < < " \n \n " ;
# endif
}
else {
std : : cerr < < ' \n ' < < errorString < < " \n \n " ;
throw RtMidiError ( errorString , type ) ;
}
}
//*********************************************************************//
// Common MidiInApi Definitions
//*********************************************************************//
MidiInApi : : MidiInApi ( unsigned int queueSizeLimit )
: MidiApi ( )
{
// Allocate the MIDI queue.
inputData_ . queue . ringSize = queueSizeLimit ;
if ( inputData_ . queue . ringSize > 0 )
inputData_ . queue . ring = new MidiMessage [ inputData_ . queue . ringSize ] ;
}
MidiInApi : : ~ MidiInApi ( void )
{
// Delete the MIDI queue.
if ( inputData_ . queue . ringSize > 0 ) delete [ ] inputData_ . queue . ring ;
}
void MidiInApi : : setCallback ( RtMidiIn : : RtMidiCallback callback , void * userData )
{
if ( inputData_ . usingCallback ) {
errorString_ = " MidiInApi::setCallback: a callback function is already set! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
if ( ! callback ) {
errorString_ = " RtMidiIn::setCallback: callback function value is invalid! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
inputData_ . userCallback = callback ;
inputData_ . userData = userData ;
inputData_ . usingCallback = true ;
}
void MidiInApi : : cancelCallback ( )
{
if ( ! inputData_ . usingCallback ) {
errorString_ = " RtMidiIn::cancelCallback: no callback function was set! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
inputData_ . userCallback = 0 ;
inputData_ . userData = 0 ;
inputData_ . usingCallback = false ;
}
void MidiInApi : : ignoreTypes ( bool midiSysex , bool midiTime , bool midiSense )
{
inputData_ . ignoreFlags = 0 ;
if ( midiSysex ) inputData_ . ignoreFlags = 0x01 ;
if ( midiTime ) inputData_ . ignoreFlags | = 0x02 ;
if ( midiSense ) inputData_ . ignoreFlags | = 0x04 ;
}
double MidiInApi : : getMessage ( std : : vector < unsigned char > * message )
{
message - > clear ( ) ;
if ( inputData_ . usingCallback ) {
errorString_ = " RtMidiIn::getNextMessage: a user callback is currently set for this port. " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return 0.0 ;
}
double timeStamp ;
if ( ! inputData_ . queue . pop ( message , & timeStamp ) )
return 0.0 ;
return timeStamp ;
}
void MidiInApi : : setBufferSize ( unsigned int size , unsigned int count )
{
inputData_ . bufferSize = size ;
inputData_ . bufferCount = count ;
}
unsigned int MidiInApi : : MidiQueue : : size ( unsigned int * __back ,
unsigned int * __front )
{
// Access back/front members exactly once and make stack copies for
// size calculation
unsigned int _back = back , _front = front , _size ;
if ( _back > = _front )
_size = _back - _front ;
else
_size = ringSize - _front + _back ;
// Return copies of back/front so no new and unsynchronized accesses
// to member variables are needed.
if ( __back ) * __back = _back ;
if ( __front ) * __front = _front ;
return _size ;
}
// As long as we haven't reached our queue size limit, push the message.
bool MidiInApi : : MidiQueue : : push ( const MidiInApi : : MidiMessage & msg )
{
// Local stack copies of front/back
unsigned int _back , _front , _size ;
// Get back/front indexes exactly once and calculate current size
_size = size ( & _back , & _front ) ;
if ( _size < ringSize - 1 )
{
ring [ _back ] = msg ;
back = ( back + 1 ) % ringSize ;
return true ;
}
return false ;
}
bool MidiInApi : : MidiQueue : : pop ( std : : vector < unsigned char > * msg , double * timeStamp )
{
// Local stack copies of front/back
unsigned int _back , _front , _size ;
// Get back/front indexes exactly once and calculate current size
_size = size ( & _back , & _front ) ;
if ( _size = = 0 )
return false ;
// Copy queued message to the vector pointer argument and then "pop" it.
msg - > assign ( ring [ _front ] . bytes . begin ( ) , ring [ _front ] . bytes . end ( ) ) ;
* timeStamp = ring [ _front ] . timeStamp ;
// Update front
front = ( front + 1 ) % ringSize ;
return true ;
}
//*********************************************************************//
// Common MidiOutApi Definitions
//*********************************************************************//
MidiOutApi : : MidiOutApi ( void )
: MidiApi ( )
{
}
MidiOutApi : : ~ MidiOutApi ( void )
{
}
// *************************************************** //
//
// OS/API-specific methods.
//
// *************************************************** //
# if defined(__MACOSX_CORE__)
// The CoreMIDI API is based on the use of a callback function for
// MIDI input. We convert the system specific time stamps to delta
// time values.
// These are not available on iOS.
2022-06-30 08:44:03 +00:00
# ifdef TARGET_OS_IPHONE
2022-02-13 20:02:43 +00:00
# include <CoreAudio/HostTime.h>
# include <CoreServices/CoreServices.h>
# endif
// A structure to hold variables related to the CoreMIDI API
// implementation.
struct CoreMidiData {
MIDIClientRef client ;
MIDIPortRef port ;
MIDIEndpointRef endpoint ;
MIDIEndpointRef destinationId ;
unsigned long long lastTime ;
MIDISysexSendRequest sysexreq ;
} ;
static MIDIClientRef CoreMidiClientSingleton = 0 ;
void RtMidi_setCoreMidiClientSingleton ( MIDIClientRef client ) {
CoreMidiClientSingleton = client ;
}
void RtMidi_disposeCoreMidiClientSingleton ( ) {
if ( CoreMidiClientSingleton = = 0 ) {
return ;
}
MIDIClientDispose ( CoreMidiClientSingleton ) ;
CoreMidiClientSingleton = 0 ;
}
//*********************************************************************//
// API: OS-X
// Class Definitions: MidiInCore
//*********************************************************************//
static void midiInputCallback ( const MIDIPacketList * list , void * procRef , void */ * srcRef */ )
{
MidiInApi : : RtMidiInData * data = static_cast < MidiInApi : : RtMidiInData * > ( procRef ) ;
CoreMidiData * apiData = static_cast < CoreMidiData * > ( data - > apiData ) ;
unsigned char status ;
unsigned short nBytes , iByte , size ;
unsigned long long time ;
bool & continueSysex = data - > continueSysex ;
MidiInApi : : MidiMessage & message = data - > message ;
const MIDIPacket * packet = & list - > packet [ 0 ] ;
for ( unsigned int i = 0 ; i < list - > numPackets ; + + i ) {
// My interpretation of the CoreMIDI documentation: all message
// types, except sysex, are complete within a packet and there may
// be several of them in a single packet. Sysex messages can be
// broken across multiple packets and PacketLists but are bundled
// alone within each packet (these packets do not contain other
// message types). If sysex messages are split across multiple
// MIDIPacketLists, they must be handled by multiple calls to this
// function.
nBytes = packet - > length ;
if ( nBytes = = 0 ) {
packet = MIDIPacketNext ( packet ) ;
continue ;
}
// Calculate time stamp.
if ( data - > firstMessage ) {
message . timeStamp = 0.0 ;
data - > firstMessage = false ;
}
else {
time = packet - > timeStamp ;
if ( time = = 0 ) { // this happens when receiving asynchronous sysex messages
time = AudioGetCurrentHostTime ( ) ;
}
time - = apiData - > lastTime ;
time = AudioConvertHostTimeToNanos ( time ) ;
if ( ! continueSysex )
message . timeStamp = time * 0.000000001 ;
}
// Track whether any non-filtered messages were found in this
// packet for timestamp calculation
bool foundNonFiltered = false ;
iByte = 0 ;
if ( continueSysex ) {
// We have a continuing, segmented sysex message.
if ( ! ( data - > ignoreFlags & 0x01 ) ) {
// If we're not ignoring sysex messages, copy the entire packet.
for ( unsigned int j = 0 ; j < nBytes ; + + j )
message . bytes . push_back ( packet - > data [ j ] ) ;
}
continueSysex = packet - > data [ nBytes - 1 ] ! = 0xF7 ;
if ( ! ( data - > ignoreFlags & 0x01 ) & & ! continueSysex ) {
// If not a continuing sysex message, invoke the user callback function or queue the message.
if ( data - > usingCallback ) {
RtMidiIn : : RtMidiCallback callback = ( RtMidiIn : : RtMidiCallback ) data - > userCallback ;
callback ( message . timeStamp , & message . bytes , data - > userData ) ;
}
else {
// As long as we haven't reached our queue size limit, push the message.
if ( ! data - > queue . push ( message ) )
std : : cerr < < " \n MidiInCore: message queue limit reached!! \n \n " ;
}
message . bytes . clear ( ) ;
}
}
else {
while ( iByte < nBytes ) {
size = 0 ;
// We are expecting that the next byte in the packet is a status byte.
status = packet - > data [ iByte ] ;
if ( ! ( status & 0x80 ) ) break ;
// Determine the number of bytes in the MIDI message.
if ( status < 0xC0 ) size = 3 ;
else if ( status < 0xE0 ) size = 2 ;
else if ( status < 0xF0 ) size = 3 ;
else if ( status = = 0xF0 ) {
// A MIDI sysex
if ( data - > ignoreFlags & 0x01 ) {
size = 0 ;
iByte = nBytes ;
}
else size = nBytes - iByte ;
continueSysex = packet - > data [ nBytes - 1 ] ! = 0xF7 ;
}
else if ( status = = 0xF1 ) {
// A MIDI time code message
if ( data - > ignoreFlags & 0x02 ) {
size = 0 ;
iByte + = 2 ;
}
else size = 2 ;
}
else if ( status = = 0xF2 ) size = 3 ;
else if ( status = = 0xF3 ) size = 2 ;
else if ( status = = 0xF8 & & ( data - > ignoreFlags & 0x02 ) ) {
// A MIDI timing tick message and we're ignoring it.
size = 0 ;
iByte + = 1 ;
}
else if ( status = = 0xFE & & ( data - > ignoreFlags & 0x04 ) ) {
// A MIDI active sensing message and we're ignoring it.
size = 0 ;
iByte + = 1 ;
}
else size = 1 ;
// Copy the MIDI data to our vector.
if ( size ) {
foundNonFiltered = true ;
message . bytes . assign ( & packet - > data [ iByte ] , & packet - > data [ iByte + size ] ) ;
if ( ! continueSysex ) {
// If not a continuing sysex message, invoke the user callback function or queue the message.
if ( data - > usingCallback ) {
RtMidiIn : : RtMidiCallback callback = ( RtMidiIn : : RtMidiCallback ) data - > userCallback ;
callback ( message . timeStamp , & message . bytes , data - > userData ) ;
}
else {
// As long as we haven't reached our queue size limit, push the message.
if ( ! data - > queue . push ( message ) )
std : : cerr < < " \n MidiInCore: message queue limit reached!! \n \n " ;
}
message . bytes . clear ( ) ;
}
iByte + = size ;
}
}
}
// Save the time of the last non-filtered message
if ( foundNonFiltered ) {
apiData - > lastTime = packet - > timeStamp ;
if ( apiData - > lastTime = = 0 ) { // this happens when receiving asynchronous sysex messages
apiData - > lastTime = AudioGetCurrentHostTime ( ) ;
}
}
packet = MIDIPacketNext ( packet ) ;
}
}
MidiInCore : : MidiInCore ( const std : : string & clientName , unsigned int queueSizeLimit )
: MidiInApi ( queueSizeLimit )
{
MidiInCore : : initialize ( clientName ) ;
}
MidiInCore : : ~ MidiInCore ( void )
{
// Close a connection if it exists.
MidiInCore : : closePort ( ) ;
// Cleanup.
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
if ( data - > endpoint ) MIDIEndpointDispose ( data - > endpoint ) ;
delete data ;
}
MIDIClientRef MidiInCore : : getCoreMidiClientSingleton ( const std : : string & clientName ) throw ( ) {
if ( CoreMidiClientSingleton = = 0 ) {
// Set up our client.
MIDIClientRef client ;
CFStringRef name = CFStringCreateWithCString ( NULL , clientName . c_str ( ) , kCFStringEncodingASCII ) ;
OSStatus result = MIDIClientCreate ( name , NULL , NULL , & client ) ;
if ( result ! = noErr ) {
std : : ostringstream ost ;
ost < < " MidiInCore::initialize: error creating OS-X MIDI client object ( " < < result < < " ). " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return 0 ;
}
CFRelease ( name ) ;
CoreMidiClientSingleton = client ;
}
return CoreMidiClientSingleton ;
}
void MidiInCore : : initialize ( const std : : string & clientName )
{
// Set up our client.
MIDIClientRef client = getCoreMidiClientSingleton ( clientName ) ;
// Save our api-specific connection information.
CoreMidiData * data = ( CoreMidiData * ) new CoreMidiData ;
data - > client = client ;
data - > endpoint = 0 ;
apiData_ = ( void * ) data ;
inputData_ . apiData = ( void * ) data ;
}
void MidiInCore : : openPort ( unsigned int portNumber , const std : : string & portName )
{
if ( connected_ ) {
errorString_ = " MidiInCore::openPort: a valid connection already exists! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 0 , false ) ;
unsigned int nSrc = MIDIGetNumberOfSources ( ) ;
if ( nSrc < 1 ) {
errorString_ = " MidiInCore::openPort: no MIDI input sources found! " ;
error ( RtMidiError : : NO_DEVICES_FOUND , errorString_ ) ;
return ;
}
if ( portNumber > = nSrc ) {
std : : ostringstream ost ;
ost < < " MidiInCore::openPort: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : INVALID_PARAMETER , errorString_ ) ;
return ;
}
MIDIPortRef port ;
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
CFStringRef portNameRef = CFStringCreateWithCString ( NULL , portName . c_str ( ) , kCFStringEncodingASCII ) ;
OSStatus result = MIDIInputPortCreate ( data - > client ,
portNameRef ,
midiInputCallback , ( void * ) & inputData_ , & port ) ;
CFRelease ( portNameRef ) ;
if ( result ! = noErr ) {
errorString_ = " MidiInCore::openPort: error creating OS-X MIDI input port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Get the desired input source identifier.
MIDIEndpointRef endpoint = MIDIGetSource ( portNumber ) ;
if ( endpoint = = 0 ) {
MIDIPortDispose ( port ) ;
errorString_ = " MidiInCore::openPort: error getting MIDI input source reference. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Make the connection.
result = MIDIPortConnectSource ( port , endpoint , NULL ) ;
if ( result ! = noErr ) {
MIDIPortDispose ( port ) ;
errorString_ = " MidiInCore::openPort: error connecting OS-X MIDI input port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Save our api-specific port information.
data - > port = port ;
connected_ = true ;
}
void MidiInCore : : openVirtualPort ( const std : : string & portName )
{
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
// Create a virtual MIDI input destination.
MIDIEndpointRef endpoint ;
CFStringRef portNameRef = CFStringCreateWithCString ( NULL , portName . c_str ( ) , kCFStringEncodingASCII ) ;
OSStatus result = MIDIDestinationCreate ( data - > client ,
portNameRef ,
midiInputCallback , ( void * ) & inputData_ , & endpoint ) ;
CFRelease ( portNameRef ) ;
if ( result ! = noErr ) {
errorString_ = " MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Save our api-specific connection information.
data - > endpoint = endpoint ;
}
void MidiInCore : : closePort ( void )
{
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
if ( data - > endpoint ) {
MIDIEndpointDispose ( data - > endpoint ) ;
data - > endpoint = 0 ;
}
if ( data - > port ) {
MIDIPortDispose ( data - > port ) ;
data - > port = 0 ;
}
connected_ = false ;
}
void MidiInCore : : setClientName ( const std : : string & )
{
errorString_ = " MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiInCore : : setPortName ( const std : : string & )
{
errorString_ = " MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
unsigned int MidiInCore : : getPortCount ( )
{
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 0 , false ) ;
return MIDIGetNumberOfSources ( ) ;
}
// This function was submitted by Douglas Casey Tucker and apparently
// derived largely from PortMidi.
CFStringRef EndpointName ( MIDIEndpointRef endpoint , bool isExternal )
{
CFMutableStringRef result = CFStringCreateMutable ( NULL , 0 ) ;
CFStringRef str ;
// Begin with the endpoint's name.
str = NULL ;
MIDIObjectGetStringProperty ( endpoint , kMIDIPropertyName , & str ) ;
if ( str ! = NULL ) {
CFStringAppend ( result , str ) ;
CFRelease ( str ) ;
}
// some MIDI devices have a leading space in endpoint name. trim
CFStringRef space = CFStringCreateWithCString ( NULL , " " , kCFStringEncodingUTF8 ) ;
CFStringTrim ( result , space ) ;
CFRelease ( space ) ;
MIDIEntityRef entity = 0 ;
MIDIEndpointGetEntity ( endpoint , & entity ) ;
if ( entity = = 0 )
// probably virtual
return result ;
if ( CFStringGetLength ( result ) = = 0 ) {
// endpoint name has zero length -- try the entity
str = NULL ;
MIDIObjectGetStringProperty ( entity , kMIDIPropertyName , & str ) ;
if ( str ! = NULL ) {
CFStringAppend ( result , str ) ;
CFRelease ( str ) ;
}
}
// now consider the device's name
MIDIDeviceRef device = 0 ;
MIDIEntityGetDevice ( entity , & device ) ;
if ( device = = 0 )
return result ;
str = NULL ;
MIDIObjectGetStringProperty ( device , kMIDIPropertyName , & str ) ;
if ( CFStringGetLength ( result ) = = 0 ) {
CFRelease ( result ) ;
return str ;
}
if ( str ! = NULL ) {
// if an external device has only one entity, throw away
// the endpoint name and just use the device name
if ( isExternal & & MIDIDeviceGetNumberOfEntities ( device ) < 2 ) {
CFRelease ( result ) ;
return str ;
} else {
if ( CFStringGetLength ( str ) = = 0 ) {
CFRelease ( str ) ;
return result ;
}
// does the entity name already start with the device name?
// (some drivers do this though they shouldn't)
// if so, do not prepend
if ( CFStringCompareWithOptions ( result , /* endpoint name */
str /* device name */ ,
CFRangeMake ( 0 , CFStringGetLength ( str ) ) , 0 ) ! = kCFCompareEqualTo ) {
// prepend the device name to the entity name
if ( CFStringGetLength ( result ) > 0 )
CFStringInsert ( result , 0 , CFSTR ( " " ) ) ;
CFStringInsert ( result , 0 , str ) ;
}
CFRelease ( str ) ;
}
}
return result ;
}
// This function was submitted by Douglas Casey Tucker and apparently
// derived largely from PortMidi.
static CFStringRef ConnectedEndpointName ( MIDIEndpointRef endpoint )
{
CFMutableStringRef result = CFStringCreateMutable ( NULL , 0 ) ;
CFStringRef str ;
OSStatus err ;
int i ;
// Does the endpoint have connections?
CFDataRef connections = NULL ;
int nConnected = 0 ;
bool anyStrings = false ;
err = MIDIObjectGetDataProperty ( endpoint , kMIDIPropertyConnectionUniqueID , & connections ) ;
if ( connections ! = NULL ) {
// It has connections, follow them
// Concatenate the names of all connected devices
nConnected = CFDataGetLength ( connections ) / sizeof ( MIDIUniqueID ) ;
if ( nConnected ) {
const SInt32 * pid = ( const SInt32 * ) ( CFDataGetBytePtr ( connections ) ) ;
for ( i = 0 ; i < nConnected ; + + i , + + pid ) {
MIDIUniqueID id = EndianS32_BtoN ( * pid ) ;
MIDIObjectRef connObject ;
MIDIObjectType connObjectType ;
err = MIDIObjectFindByUniqueID ( id , & connObject , & connObjectType ) ;
if ( err = = noErr ) {
if ( connObjectType = = kMIDIObjectType_ExternalSource | |
connObjectType = = kMIDIObjectType_ExternalDestination ) {
// Connected to an external device's endpoint (10.3 and later).
str = EndpointName ( ( MIDIEndpointRef ) ( connObject ) , true ) ;
} else {
// Connected to an external device (10.2) (or something else, catch-
str = NULL ;
MIDIObjectGetStringProperty ( connObject , kMIDIPropertyName , & str ) ;
}
if ( str ! = NULL ) {
if ( anyStrings )
CFStringAppend ( result , CFSTR ( " , " ) ) ;
else
anyStrings = true ;
CFStringAppend ( result , str ) ;
CFRelease ( str ) ;
}
}
}
}
CFRelease ( connections ) ;
}
if ( anyStrings )
return result ;
CFRelease ( result ) ;
// Here, either the endpoint had no connections, or we failed to obtain names
return EndpointName ( endpoint , false ) ;
}
std : : string MidiInCore : : getPortName ( unsigned int portNumber )
{
CFStringRef nameRef ;
MIDIEndpointRef portRef ;
char name [ 128 ] ;
std : : string stringName ;
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 0 , false ) ;
if ( portNumber > = MIDIGetNumberOfSources ( ) ) {
std : : ostringstream ost ;
ost < < " MidiInCore::getPortName: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return stringName ;
}
portRef = MIDIGetSource ( portNumber ) ;
nameRef = ConnectedEndpointName ( portRef ) ;
CFStringGetCString ( nameRef , name , sizeof ( name ) , kCFStringEncodingUTF8 ) ;
CFRelease ( nameRef ) ;
return stringName = name ;
}
//*********************************************************************//
// API: OS-X
// Class Definitions: MidiOutCore
//*********************************************************************//
MidiOutCore : : MidiOutCore ( const std : : string & clientName )
: MidiOutApi ( )
{
MidiOutCore : : initialize ( clientName ) ;
}
MidiOutCore : : ~ MidiOutCore ( void )
{
// Close a connection if it exists.
MidiOutCore : : closePort ( ) ;
// Cleanup.
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
if ( data - > endpoint ) MIDIEndpointDispose ( data - > endpoint ) ;
delete data ;
}
MIDIClientRef MidiOutCore : : getCoreMidiClientSingleton ( const std : : string & clientName ) throw ( ) {
if ( CoreMidiClientSingleton = = 0 ) {
// Set up our client.
MIDIClientRef client ;
CFStringRef name = CFStringCreateWithCString ( NULL , clientName . c_str ( ) , kCFStringEncodingASCII ) ;
OSStatus result = MIDIClientCreate ( name , NULL , NULL , & client ) ;
if ( result ! = noErr ) {
std : : ostringstream ost ;
ost < < " MidiInCore::initialize: error creating OS-X MIDI client object ( " < < result < < " ). " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return 0 ;
}
CFRelease ( name ) ;
CoreMidiClientSingleton = client ;
}
return CoreMidiClientSingleton ;
}
void MidiOutCore : : initialize ( const std : : string & clientName )
{
// Set up our client.
MIDIClientRef client = getCoreMidiClientSingleton ( clientName ) ;
// Save our api-specific connection information.
CoreMidiData * data = ( CoreMidiData * ) new CoreMidiData ;
data - > client = client ;
data - > endpoint = 0 ;
apiData_ = ( void * ) data ;
}
unsigned int MidiOutCore : : getPortCount ( )
{
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 0 , false ) ;
return MIDIGetNumberOfDestinations ( ) ;
}
std : : string MidiOutCore : : getPortName ( unsigned int portNumber )
{
CFStringRef nameRef ;
MIDIEndpointRef portRef ;
char name [ 128 ] ;
std : : string stringName ;
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 0 , false ) ;
if ( portNumber > = MIDIGetNumberOfDestinations ( ) ) {
std : : ostringstream ost ;
ost < < " MidiOutCore::getPortName: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return stringName ;
}
portRef = MIDIGetDestination ( portNumber ) ;
nameRef = ConnectedEndpointName ( portRef ) ;
CFStringGetCString ( nameRef , name , sizeof ( name ) , kCFStringEncodingUTF8 ) ;
CFRelease ( nameRef ) ;
return stringName = name ;
}
void MidiOutCore : : openPort ( unsigned int portNumber , const std : : string & portName )
{
if ( connected_ ) {
errorString_ = " MidiOutCore::openPort: a valid connection already exists! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 0 , false ) ;
unsigned int nDest = MIDIGetNumberOfDestinations ( ) ;
if ( nDest < 1 ) {
errorString_ = " MidiOutCore::openPort: no MIDI output destinations found! " ;
error ( RtMidiError : : NO_DEVICES_FOUND , errorString_ ) ;
return ;
}
if ( portNumber > = nDest ) {
std : : ostringstream ost ;
ost < < " MidiOutCore::openPort: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : INVALID_PARAMETER , errorString_ ) ;
return ;
}
MIDIPortRef port ;
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
CFStringRef portNameRef = CFStringCreateWithCString ( NULL , portName . c_str ( ) , kCFStringEncodingASCII ) ;
OSStatus result = MIDIOutputPortCreate ( data - > client , portNameRef , & port ) ;
CFRelease ( portNameRef ) ;
if ( result ! = noErr ) {
errorString_ = " MidiOutCore::openPort: error creating OS-X MIDI output port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Get the desired output port identifier.
MIDIEndpointRef destination = MIDIGetDestination ( portNumber ) ;
if ( destination = = 0 ) {
MIDIPortDispose ( port ) ;
errorString_ = " MidiOutCore::openPort: error getting MIDI output destination reference. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Save our api-specific connection information.
data - > port = port ;
data - > destinationId = destination ;
connected_ = true ;
}
void MidiOutCore : : closePort ( void )
{
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
if ( data - > endpoint ) {
MIDIEndpointDispose ( data - > endpoint ) ;
data - > endpoint = 0 ;
}
if ( data - > port ) {
MIDIPortDispose ( data - > port ) ;
data - > port = 0 ;
}
connected_ = false ;
}
void MidiOutCore : : setClientName ( const std : : string & )
{
errorString_ = " MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiOutCore : : setPortName ( const std : : string & )
{
errorString_ = " MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiOutCore : : openVirtualPort ( const std : : string & portName )
{
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
if ( data - > endpoint ) {
errorString_ = " MidiOutCore::openVirtualPort: a virtual output port already exists! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
// Create a virtual MIDI output source.
MIDIEndpointRef endpoint ;
CFStringRef portNameRef = CFStringCreateWithCString ( NULL , portName . c_str ( ) , kCFStringEncodingASCII ) ;
OSStatus result = MIDISourceCreate ( data - > client , portNameRef , & endpoint ) ;
CFRelease ( portNameRef ) ;
if ( result ! = noErr ) {
errorString_ = " MidiOutCore::initialize: error creating OS-X virtual MIDI source. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Save our api-specific connection information.
data - > endpoint = endpoint ;
}
void MidiOutCore : : sendMessage ( const unsigned char * message , size_t size )
{
// We use the MIDISendSysex() function to asynchronously send sysex
// messages. Otherwise, we use a single CoreMidi MIDIPacket.
unsigned int nBytes = static_cast < unsigned int > ( size ) ;
if ( nBytes = = 0 ) {
errorString_ = " MidiOutCore::sendMessage: no data in message argument! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
if ( message [ 0 ] ! = 0xF0 & & nBytes > 3 ) {
errorString_ = " MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes? " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
MIDITimeStamp timeStamp = AudioGetCurrentHostTime ( ) ;
CoreMidiData * data = static_cast < CoreMidiData * > ( apiData_ ) ;
OSStatus result ;
ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes ;
Byte buffer [ bufsize + 16 ] ; // pad for other struct members
ByteCount listSize = sizeof ( buffer ) ;
MIDIPacketList * packetList = ( MIDIPacketList * ) buffer ;
ByteCount remainingBytes = nBytes ;
while ( remainingBytes ) {
MIDIPacket * packet = MIDIPacketListInit ( packetList ) ;
// A MIDIPacketList can only contain a maximum of 64K of data, so if our message is longer,
// break it up into chunks of 64K or less and send out as a MIDIPacketList with only one
// MIDIPacket. Here, we reuse the memory allocated above on the stack for all.
ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes ;
const Byte * dataStartPtr = ( const Byte * ) & message [ nBytes - remainingBytes ] ;
packet = MIDIPacketListAdd ( packetList , listSize , packet , timeStamp , bytesForPacket , dataStartPtr ) ;
remainingBytes - = bytesForPacket ;
if ( ! packet ) {
errorString_ = " MidiOutCore::sendMessage: could not allocate packet list " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Send to any destinations that may have connected to us.
if ( data - > endpoint ) {
result = MIDIReceived ( data - > endpoint , packetList ) ;
if ( result ! = noErr ) {
errorString_ = " MidiOutCore::sendMessage: error sending MIDI to virtual destinations. " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
}
// And send to an explicit destination port if we're connected.
if ( connected_ ) {
result = MIDISend ( data - > port , data - > destinationId , packetList ) ;
if ( result ! = noErr ) {
errorString_ = " MidiOutCore::sendMessage: error sending MIDI message to port. " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
}
}
}
# endif // __MACOSX_CORE__
//*********************************************************************//
// API: LINUX ALSA SEQUENCER
//*********************************************************************//
// API information found at:
// - http://www.alsa-project.org/documentation.php#Library
# if defined(__LINUX_ALSA__)
// The ALSA Sequencer API is based on the use of a callback function for
// MIDI input.
//
// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
// time stamps and other assorted fixes!!!
// If you don't need timestamping for incoming MIDI events, define the
// preprocessor definition AVOID_TIMESTAMPING to save resources
// associated with the ALSA sequencer queues.
# include <pthread.h>
# include <sys/time.h>
// ALSA header file.
# include <alsa/asoundlib.h>
// A structure to hold variables related to the ALSA API
// implementation.
struct AlsaMidiData {
snd_seq_t * seq ;
unsigned int portNum ;
int vport ;
snd_seq_port_subscribe_t * subscription ;
snd_midi_event_t * coder ;
unsigned int bufferSize ;
unsigned int requestedBufferSize ;
unsigned char * buffer ;
pthread_t thread ;
pthread_t dummy_thread_id ;
snd_seq_real_time_t lastTime ;
int queue_id ; // an input queue is needed to get timestamped events
int trigger_fds [ 2 ] ;
} ;
# define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
//*********************************************************************//
// API: LINUX ALSA
// Class Definitions: MidiInAlsa
//*********************************************************************//
static void * alsaMidiHandler ( void * ptr )
{
MidiInApi : : RtMidiInData * data = static_cast < MidiInApi : : RtMidiInData * > ( ptr ) ;
AlsaMidiData * apiData = static_cast < AlsaMidiData * > ( data - > apiData ) ;
long nBytes ;
double time ;
bool continueSysex = false ;
bool doDecode = false ;
MidiInApi : : MidiMessage message ;
int poll_fd_count ;
struct pollfd * poll_fds ;
snd_seq_event_t * ev ;
int result ;
result = snd_midi_event_new ( 0 , & apiData - > coder ) ;
if ( result < 0 ) {
data - > doInput = false ;
std : : cerr < < " \n MidiInAlsa::alsaMidiHandler: error initializing MIDI event parser! \n \n " ;
return 0 ;
}
unsigned char * buffer = ( unsigned char * ) malloc ( apiData - > bufferSize ) ;
if ( buffer = = NULL ) {
data - > doInput = false ;
snd_midi_event_free ( apiData - > coder ) ;
apiData - > coder = 0 ;
std : : cerr < < " \n MidiInAlsa::alsaMidiHandler: error initializing buffer memory! \n \n " ;
return 0 ;
}
snd_midi_event_init ( apiData - > coder ) ;
snd_midi_event_no_status ( apiData - > coder , 1 ) ; // suppress running status messages
poll_fd_count = snd_seq_poll_descriptors_count ( apiData - > seq , POLLIN ) + 1 ;
poll_fds = ( struct pollfd * ) alloca ( poll_fd_count * sizeof ( struct pollfd ) ) ;
snd_seq_poll_descriptors ( apiData - > seq , poll_fds + 1 , poll_fd_count - 1 , POLLIN ) ;
poll_fds [ 0 ] . fd = apiData - > trigger_fds [ 0 ] ;
poll_fds [ 0 ] . events = POLLIN ;
while ( data - > doInput ) {
if ( snd_seq_event_input_pending ( apiData - > seq , 1 ) = = 0 ) {
// No data pending
if ( poll ( poll_fds , poll_fd_count , - 1 ) > = 0 ) {
if ( poll_fds [ 0 ] . revents & POLLIN ) {
bool dummy ;
int res = read ( poll_fds [ 0 ] . fd , & dummy , sizeof ( dummy ) ) ;
( void ) res ;
}
}
continue ;
}
// If here, there should be data.
result = snd_seq_event_input ( apiData - > seq , & ev ) ;
if ( result = = - ENOSPC ) {
std : : cerr < < " \n MidiInAlsa::alsaMidiHandler: MIDI input buffer overrun! \n \n " ;
continue ;
}
else if ( result < = 0 ) {
std : : cerr < < " \n MidiInAlsa::alsaMidiHandler: unknown MIDI input error! \n " ;
perror ( " System reports " ) ;
continue ;
}
// This is a bit weird, but we now have to decode an ALSA MIDI
// event (back) into MIDI bytes. We'll ignore non-MIDI types.
if ( ! continueSysex ) message . bytes . clear ( ) ;
doDecode = false ;
switch ( ev - > type ) {
case SND_SEQ_EVENT_PORT_SUBSCRIBED :
# if defined(__RTMIDI_DEBUG__)
2024-04-23 09:38:08 +00:00
std : : cerr < < " MidiInAlsa::alsaMidiHandler: port connection made! \n " ;
2022-02-13 20:02:43 +00:00
# endif
break ;
case SND_SEQ_EVENT_PORT_UNSUBSCRIBED :
# if defined(__RTMIDI_DEBUG__)
std : : cerr < < " MidiInAlsa::alsaMidiHandler: port connection has closed! \n " ;
2024-04-23 09:38:08 +00:00
std : : cerr < < " sender = " < < ( int ) ev - > data . connect . sender . client < < " : "
2022-02-13 20:02:43 +00:00
< < ( int ) ev - > data . connect . sender . port
< < " , dest = " < < ( int ) ev - > data . connect . dest . client < < " : "
< < ( int ) ev - > data . connect . dest . port
< < std : : endl ;
# endif
break ;
case SND_SEQ_EVENT_QFRAME : // MIDI time code
if ( ! ( data - > ignoreFlags & 0x02 ) ) doDecode = true ;
break ;
case SND_SEQ_EVENT_TICK : // 0xF9 ... MIDI timing tick
if ( ! ( data - > ignoreFlags & 0x02 ) ) doDecode = true ;
break ;
case SND_SEQ_EVENT_CLOCK : // 0xF8 ... MIDI timing (clock) tick
if ( ! ( data - > ignoreFlags & 0x02 ) ) doDecode = true ;
break ;
case SND_SEQ_EVENT_SENSING : // Active sensing
if ( ! ( data - > ignoreFlags & 0x04 ) ) doDecode = true ;
break ;
case SND_SEQ_EVENT_SYSEX :
if ( ( data - > ignoreFlags & 0x01 ) ) break ;
if ( ev - > data . ext . len > apiData - > bufferSize ) {
apiData - > bufferSize = ev - > data . ext . len ;
free ( buffer ) ;
buffer = ( unsigned char * ) malloc ( apiData - > bufferSize ) ;
if ( buffer = = NULL ) {
data - > doInput = false ;
std : : cerr < < " \n MidiInAlsa::alsaMidiHandler: error resizing buffer memory! \n \n " ;
break ;
}
}
doDecode = true ;
break ;
default :
doDecode = true ;
}
if ( doDecode ) {
nBytes = snd_midi_event_decode ( apiData - > coder , buffer , apiData - > bufferSize , ev ) ;
if ( nBytes > 0 ) {
// The ALSA sequencer has a maximum buffer size for MIDI sysex
// events of 256 bytes. If a device sends sysex messages larger
// than this, they are segmented into 256 byte chunks. So,
// we'll watch for this and concatenate sysex chunks into a
// single sysex message if necessary.
if ( ! continueSysex )
message . bytes . assign ( buffer , & buffer [ nBytes ] ) ;
else
message . bytes . insert ( message . bytes . end ( ) , buffer , & buffer [ nBytes ] ) ;
continueSysex = ( ( ev - > type = = SND_SEQ_EVENT_SYSEX ) & & ( message . bytes . back ( ) ! = 0xF7 ) ) ;
if ( ! continueSysex ) {
// Calculate the time stamp:
message . timeStamp = 0.0 ;
// Method 1: Use the system time.
//(void)gettimeofday(&tv, (struct timezone *)NULL);
//time = (tv.tv_sec * 1000000) + tv.tv_usec;
// Method 2: Use the ALSA sequencer event time data.
// (thanks to Pedro Lopez-Cabanillas!).
// Using method from:
// https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
// Perform the carry for the later subtraction by updating y.
// Temp var y is timespec because computation requires signed types,
// while snd_seq_real_time_t has unsigned types.
snd_seq_real_time_t & x ( ev - > time . time ) ;
struct timespec y ;
y . tv_nsec = apiData - > lastTime . tv_nsec ;
y . tv_sec = apiData - > lastTime . tv_sec ;
2024-05-01 19:46:58 +00:00
if ( ( long int ) x . tv_nsec < y . tv_nsec ) {
2022-02-13 20:02:43 +00:00
int nsec = ( y . tv_nsec - ( int ) x . tv_nsec ) / 1000000000 + 1 ;
y . tv_nsec - = 1000000000 * nsec ;
y . tv_sec + = nsec ;
}
if ( x . tv_nsec - y . tv_nsec > 1000000000 ) {
int nsec = ( ( int ) x . tv_nsec - y . tv_nsec ) / 1000000000 ;
y . tv_nsec + = 1000000000 * nsec ;
y . tv_sec - = nsec ;
}
// Compute the time difference.
time = ( int ) x . tv_sec - y . tv_sec + ( ( int ) x . tv_nsec - y . tv_nsec ) * 1e-9 ;
apiData - > lastTime = ev - > time . time ;
if ( data - > firstMessage = = true )
data - > firstMessage = false ;
else
message . timeStamp = time ;
}
else {
# if defined(__RTMIDI_DEBUG__)
std : : cerr < < " \n MidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event! \n \n " ;
# endif
}
}
}
snd_seq_free_event ( ev ) ;
if ( message . bytes . size ( ) = = 0 | | continueSysex ) continue ;
if ( data - > usingCallback ) {
RtMidiIn : : RtMidiCallback callback = ( RtMidiIn : : RtMidiCallback ) data - > userCallback ;
callback ( message . timeStamp , & message . bytes , data - > userData ) ;
}
else {
// As long as we haven't reached our queue size limit, push the message.
if ( ! data - > queue . push ( message ) )
std : : cerr < < " \n MidiInAlsa: message queue limit reached!! \n \n " ;
}
}
if ( buffer ) free ( buffer ) ;
snd_midi_event_free ( apiData - > coder ) ;
apiData - > coder = 0 ;
apiData - > thread = apiData - > dummy_thread_id ;
return 0 ;
}
MidiInAlsa : : MidiInAlsa ( const std : : string & clientName , unsigned int queueSizeLimit )
: MidiInApi ( queueSizeLimit )
{
MidiInAlsa : : initialize ( clientName ) ;
}
MidiInAlsa : : ~ MidiInAlsa ( )
{
// Close a connection if it exists.
MidiInAlsa : : closePort ( ) ;
// Shutdown the input thread.
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( inputData_ . doInput ) {
inputData_ . doInput = false ;
int res = write ( data - > trigger_fds [ 1 ] , & inputData_ . doInput , sizeof ( inputData_ . doInput ) ) ;
( void ) res ;
if ( ! pthread_equal ( data - > thread , data - > dummy_thread_id ) )
pthread_join ( data - > thread , NULL ) ;
}
// Cleanup.
close ( data - > trigger_fds [ 0 ] ) ;
close ( data - > trigger_fds [ 1 ] ) ;
if ( data - > vport > = 0 ) snd_seq_delete_port ( data - > seq , data - > vport ) ;
# ifndef AVOID_TIMESTAMPING
snd_seq_free_queue ( data - > seq , data - > queue_id ) ;
# endif
snd_seq_close ( data - > seq ) ;
delete data ;
}
void MidiInAlsa : : initialize ( const std : : string & clientName )
{
// Set up the ALSA sequencer client.
snd_seq_t * seq ;
int result = snd_seq_open ( & seq , " default " , SND_SEQ_OPEN_DUPLEX , SND_SEQ_NONBLOCK ) ;
if ( result < 0 ) {
errorString_ = " MidiInAlsa::initialize: error creating ALSA sequencer client object. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Set client name.
snd_seq_set_client_name ( seq , clientName . c_str ( ) ) ;
// Save our api-specific connection information.
AlsaMidiData * data = ( AlsaMidiData * ) new AlsaMidiData ;
data - > seq = seq ;
data - > portNum = - 1 ;
data - > vport = - 1 ;
data - > subscription = 0 ;
data - > dummy_thread_id = pthread_self ( ) ;
data - > thread = data - > dummy_thread_id ;
data - > trigger_fds [ 0 ] = - 1 ;
data - > trigger_fds [ 1 ] = - 1 ;
data - > bufferSize = inputData_ . bufferSize ;
apiData_ = ( void * ) data ;
inputData_ . apiData = ( void * ) data ;
if ( pipe ( data - > trigger_fds ) = = - 1 ) {
errorString_ = " MidiInAlsa::initialize: error creating pipe objects. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Create the input queue
# ifndef AVOID_TIMESTAMPING
data - > queue_id = snd_seq_alloc_named_queue ( seq , " RtMidi Queue " ) ;
// Set arbitrary tempo (mm=100) and resolution (240)
snd_seq_queue_tempo_t * qtempo ;
snd_seq_queue_tempo_alloca ( & qtempo ) ;
snd_seq_queue_tempo_set_tempo ( qtempo , 600000 ) ;
snd_seq_queue_tempo_set_ppq ( qtempo , 240 ) ;
snd_seq_set_queue_tempo ( data - > seq , data - > queue_id , qtempo ) ;
snd_seq_drain_output ( data - > seq ) ;
# endif
}
// This function is used to count or get the pinfo structure for a given port number.
unsigned int portInfo ( snd_seq_t * seq , snd_seq_port_info_t * pinfo , unsigned int type , int portNumber )
{
snd_seq_client_info_t * cinfo ;
int client ;
int count = 0 ;
snd_seq_client_info_alloca ( & cinfo ) ;
snd_seq_client_info_set_client ( cinfo , - 1 ) ;
while ( snd_seq_query_next_client ( seq , cinfo ) > = 0 ) {
client = snd_seq_client_info_get_client ( cinfo ) ;
if ( client = = 0 ) continue ;
// Reset query info
snd_seq_port_info_set_client ( pinfo , client ) ;
snd_seq_port_info_set_port ( pinfo , - 1 ) ;
while ( snd_seq_query_next_port ( seq , pinfo ) > = 0 ) {
unsigned int atyp = snd_seq_port_info_get_type ( pinfo ) ;
if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) = = 0 ) & &
( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) = = 0 ) & &
( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) = = 0 ) ) continue ;
unsigned int caps = snd_seq_port_info_get_capability ( pinfo ) ;
if ( ( caps & type ) ! = type ) continue ;
if ( count = = portNumber ) return 1 ;
+ + count ;
}
}
// If a negative portNumber was used, return the port count.
if ( portNumber < 0 ) return count ;
return 0 ;
}
unsigned int MidiInAlsa : : getPortCount ( )
{
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
return portInfo ( data - > seq , pinfo , SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ , - 1 ) ;
}
std : : string MidiInAlsa : : getPortName ( unsigned int portNumber )
{
snd_seq_client_info_t * cinfo ;
snd_seq_port_info_t * pinfo ;
snd_seq_client_info_alloca ( & cinfo ) ;
snd_seq_port_info_alloca ( & pinfo ) ;
std : : string stringName ;
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( portInfo ( data - > seq , pinfo , SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ , ( int ) portNumber ) ) {
int cnum = snd_seq_port_info_get_client ( pinfo ) ;
snd_seq_get_any_client_info ( data - > seq , cnum , cinfo ) ;
std : : ostringstream os ;
os < < snd_seq_client_info_get_name ( cinfo ) ;
os < < " : " ;
os < < snd_seq_port_info_get_name ( pinfo ) ;
os < < " " ; // These lines added to make sure devices are listed
os < < snd_seq_port_info_get_client ( pinfo ) ; // with full portnames added to ensure individual device names
os < < " : " ;
os < < snd_seq_port_info_get_port ( pinfo ) ;
stringName = os . str ( ) ;
return stringName ;
}
// If we get here, we didn't find a match.
errorString_ = " MidiInAlsa::getPortName: error looking for port name! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return stringName ;
}
void MidiInAlsa : : openPort ( unsigned int portNumber , const std : : string & portName )
{
if ( connected_ ) {
errorString_ = " MidiInAlsa::openPort: a valid connection already exists! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
unsigned int nSrc = this - > getPortCount ( ) ;
if ( nSrc < 1 ) {
errorString_ = " MidiInAlsa::openPort: no MIDI input sources found! " ;
error ( RtMidiError : : NO_DEVICES_FOUND , errorString_ ) ;
return ;
}
snd_seq_port_info_t * src_pinfo ;
snd_seq_port_info_alloca ( & src_pinfo ) ;
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( portInfo ( data - > seq , src_pinfo , SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ , ( int ) portNumber ) = = 0 ) {
std : : ostringstream ost ;
ost < < " MidiInAlsa::openPort: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : INVALID_PARAMETER , errorString_ ) ;
return ;
}
snd_seq_addr_t sender , receiver ;
sender . client = snd_seq_port_info_get_client ( src_pinfo ) ;
sender . port = snd_seq_port_info_get_port ( src_pinfo ) ;
receiver . client = snd_seq_client_id ( data - > seq ) ;
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
if ( data - > vport < 0 ) {
snd_seq_port_info_set_client ( pinfo , 0 ) ;
snd_seq_port_info_set_port ( pinfo , 0 ) ;
snd_seq_port_info_set_capability ( pinfo ,
SND_SEQ_PORT_CAP_WRITE |
SND_SEQ_PORT_CAP_SUBS_WRITE ) ;
snd_seq_port_info_set_type ( pinfo ,
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
SND_SEQ_PORT_TYPE_APPLICATION ) ;
snd_seq_port_info_set_midi_channels ( pinfo , 16 ) ;
# ifndef AVOID_TIMESTAMPING
snd_seq_port_info_set_timestamping ( pinfo , 1 ) ;
snd_seq_port_info_set_timestamp_real ( pinfo , 1 ) ;
snd_seq_port_info_set_timestamp_queue ( pinfo , data - > queue_id ) ;
# endif
snd_seq_port_info_set_name ( pinfo , portName . c_str ( ) ) ;
data - > vport = snd_seq_create_port ( data - > seq , pinfo ) ;
if ( data - > vport < 0 ) {
errorString_ = " MidiInAlsa::openPort: ALSA error creating input port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
data - > vport = snd_seq_port_info_get_port ( pinfo ) ;
}
receiver . port = data - > vport ;
if ( ! data - > subscription ) {
// Make subscription
if ( snd_seq_port_subscribe_malloc ( & data - > subscription ) < 0 ) {
errorString_ = " MidiInAlsa::openPort: ALSA error allocation port subscription. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
snd_seq_port_subscribe_set_sender ( data - > subscription , & sender ) ;
snd_seq_port_subscribe_set_dest ( data - > subscription , & receiver ) ;
if ( snd_seq_subscribe_port ( data - > seq , data - > subscription ) ) {
snd_seq_port_subscribe_free ( data - > subscription ) ;
data - > subscription = 0 ;
errorString_ = " MidiInAlsa::openPort: ALSA error making port connection. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
}
if ( inputData_ . doInput = = false ) {
// Start the input queue
# ifndef AVOID_TIMESTAMPING
snd_seq_start_queue ( data - > seq , data - > queue_id , NULL ) ;
snd_seq_drain_output ( data - > seq ) ;
# endif
// Start our MIDI input thread.
pthread_attr_t attr ;
pthread_attr_init ( & attr ) ;
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_JOINABLE ) ;
pthread_attr_setschedpolicy ( & attr , SCHED_OTHER ) ;
inputData_ . doInput = true ;
int err = pthread_create ( & data - > thread , & attr , alsaMidiHandler , & inputData_ ) ;
pthread_attr_destroy ( & attr ) ;
if ( err ) {
snd_seq_unsubscribe_port ( data - > seq , data - > subscription ) ;
snd_seq_port_subscribe_free ( data - > subscription ) ;
data - > subscription = 0 ;
inputData_ . doInput = false ;
errorString_ = " MidiInAlsa::openPort: error starting MIDI input thread! " ;
error ( RtMidiError : : THREAD_ERROR , errorString_ ) ;
return ;
}
}
connected_ = true ;
}
void MidiInAlsa : : openVirtualPort ( const std : : string & portName )
{
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( data - > vport < 0 ) {
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
snd_seq_port_info_set_capability ( pinfo ,
SND_SEQ_PORT_CAP_WRITE |
SND_SEQ_PORT_CAP_SUBS_WRITE ) ;
snd_seq_port_info_set_type ( pinfo ,
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
SND_SEQ_PORT_TYPE_APPLICATION ) ;
snd_seq_port_info_set_midi_channels ( pinfo , 16 ) ;
# ifndef AVOID_TIMESTAMPING
snd_seq_port_info_set_timestamping ( pinfo , 1 ) ;
snd_seq_port_info_set_timestamp_real ( pinfo , 1 ) ;
snd_seq_port_info_set_timestamp_queue ( pinfo , data - > queue_id ) ;
# endif
snd_seq_port_info_set_name ( pinfo , portName . c_str ( ) ) ;
data - > vport = snd_seq_create_port ( data - > seq , pinfo ) ;
if ( data - > vport < 0 ) {
errorString_ = " MidiInAlsa::openVirtualPort: ALSA error creating virtual port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
data - > vport = snd_seq_port_info_get_port ( pinfo ) ;
}
if ( inputData_ . doInput = = false ) {
// Wait for old thread to stop, if still running
if ( ! pthread_equal ( data - > thread , data - > dummy_thread_id ) )
pthread_join ( data - > thread , NULL ) ;
// Start the input queue
# ifndef AVOID_TIMESTAMPING
snd_seq_start_queue ( data - > seq , data - > queue_id , NULL ) ;
snd_seq_drain_output ( data - > seq ) ;
# endif
// Start our MIDI input thread.
pthread_attr_t attr ;
pthread_attr_init ( & attr ) ;
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_JOINABLE ) ;
pthread_attr_setschedpolicy ( & attr , SCHED_OTHER ) ;
inputData_ . doInput = true ;
int err = pthread_create ( & data - > thread , & attr , alsaMidiHandler , & inputData_ ) ;
pthread_attr_destroy ( & attr ) ;
if ( err ) {
if ( data - > subscription ) {
snd_seq_unsubscribe_port ( data - > seq , data - > subscription ) ;
snd_seq_port_subscribe_free ( data - > subscription ) ;
data - > subscription = 0 ;
}
inputData_ . doInput = false ;
errorString_ = " MidiInAlsa::openPort: error starting MIDI input thread! " ;
error ( RtMidiError : : THREAD_ERROR , errorString_ ) ;
return ;
}
}
}
void MidiInAlsa : : closePort ( void )
{
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( connected_ ) {
if ( data - > subscription ) {
snd_seq_unsubscribe_port ( data - > seq , data - > subscription ) ;
snd_seq_port_subscribe_free ( data - > subscription ) ;
data - > subscription = 0 ;
}
// Stop the input queue
# ifndef AVOID_TIMESTAMPING
snd_seq_stop_queue ( data - > seq , data - > queue_id , NULL ) ;
snd_seq_drain_output ( data - > seq ) ;
# endif
connected_ = false ;
}
// Stop thread to avoid triggering the callback, while the port is intended to be closed
if ( inputData_ . doInput ) {
inputData_ . doInput = false ;
int res = write ( data - > trigger_fds [ 1 ] , & inputData_ . doInput , sizeof ( inputData_ . doInput ) ) ;
( void ) res ;
if ( ! pthread_equal ( data - > thread , data - > dummy_thread_id ) )
pthread_join ( data - > thread , NULL ) ;
}
}
void MidiInAlsa : : setClientName ( const std : : string & clientName )
{
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
snd_seq_set_client_name ( data - > seq , clientName . c_str ( ) ) ;
}
void MidiInAlsa : : setPortName ( const std : : string & portName )
{
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
snd_seq_get_port_info ( data - > seq , data - > vport , pinfo ) ;
snd_seq_port_info_set_name ( pinfo , portName . c_str ( ) ) ;
snd_seq_set_port_info ( data - > seq , data - > vport , pinfo ) ;
}
//*********************************************************************//
// API: LINUX ALSA
// Class Definitions: MidiOutAlsa
//*********************************************************************//
MidiOutAlsa : : MidiOutAlsa ( const std : : string & clientName ) : MidiOutApi ( )
{
MidiOutAlsa : : initialize ( clientName ) ;
}
MidiOutAlsa : : ~ MidiOutAlsa ( )
{
// Close a connection if it exists.
MidiOutAlsa : : closePort ( ) ;
// Cleanup.
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( data - > vport > = 0 ) snd_seq_delete_port ( data - > seq , data - > vport ) ;
if ( data - > coder ) snd_midi_event_free ( data - > coder ) ;
if ( data - > buffer ) free ( data - > buffer ) ;
snd_seq_close ( data - > seq ) ;
delete data ;
}
void MidiOutAlsa : : initialize ( const std : : string & clientName )
{
// Set up the ALSA sequencer client.
snd_seq_t * seq ;
int result1 = snd_seq_open ( & seq , " default " , SND_SEQ_OPEN_OUTPUT , SND_SEQ_NONBLOCK ) ;
if ( result1 < 0 ) {
errorString_ = " MidiOutAlsa::initialize: error creating ALSA sequencer client object. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Set client name.
snd_seq_set_client_name ( seq , clientName . c_str ( ) ) ;
// Save our api-specific connection information.
AlsaMidiData * data = ( AlsaMidiData * ) new AlsaMidiData ;
data - > seq = seq ;
data - > portNum = - 1 ;
data - > vport = - 1 ;
data - > bufferSize = 32 ;
data - > coder = 0 ;
data - > buffer = 0 ;
int result = snd_midi_event_new ( data - > bufferSize , & data - > coder ) ;
if ( result < 0 ) {
delete data ;
errorString_ = " MidiOutAlsa::initialize: error initializing MIDI event parser! \n \n " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
data - > buffer = ( unsigned char * ) malloc ( data - > bufferSize ) ;
if ( data - > buffer = = NULL ) {
delete data ;
errorString_ = " MidiOutAlsa::initialize: error allocating buffer memory! \n \n " ;
error ( RtMidiError : : MEMORY_ERROR , errorString_ ) ;
return ;
}
snd_midi_event_init ( data - > coder ) ;
apiData_ = ( void * ) data ;
}
unsigned int MidiOutAlsa : : getPortCount ( )
{
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
return portInfo ( data - > seq , pinfo , SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE , - 1 ) ;
}
std : : string MidiOutAlsa : : getPortName ( unsigned int portNumber )
{
snd_seq_client_info_t * cinfo ;
snd_seq_port_info_t * pinfo ;
snd_seq_client_info_alloca ( & cinfo ) ;
snd_seq_port_info_alloca ( & pinfo ) ;
std : : string stringName ;
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( portInfo ( data - > seq , pinfo , SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE , ( int ) portNumber ) ) {
int cnum = snd_seq_port_info_get_client ( pinfo ) ;
snd_seq_get_any_client_info ( data - > seq , cnum , cinfo ) ;
std : : ostringstream os ;
os < < snd_seq_client_info_get_name ( cinfo ) ;
os < < " : " ;
os < < snd_seq_port_info_get_name ( pinfo ) ;
os < < " " ; // These lines added to make sure devices are listed
os < < snd_seq_port_info_get_client ( pinfo ) ; // with full portnames added to ensure individual device names
os < < " : " ;
os < < snd_seq_port_info_get_port ( pinfo ) ;
stringName = os . str ( ) ;
return stringName ;
}
// If we get here, we didn't find a match.
errorString_ = " MidiOutAlsa::getPortName: error looking for port name! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return stringName ;
}
void MidiOutAlsa : : openPort ( unsigned int portNumber , const std : : string & portName )
{
if ( connected_ ) {
errorString_ = " MidiOutAlsa::openPort: a valid connection already exists! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
unsigned int nSrc = this - > getPortCount ( ) ;
if ( nSrc < 1 ) {
errorString_ = " MidiOutAlsa::openPort: no MIDI output sources found! " ;
error ( RtMidiError : : NO_DEVICES_FOUND , errorString_ ) ;
return ;
}
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( portInfo ( data - > seq , pinfo , SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE , ( int ) portNumber ) = = 0 ) {
std : : ostringstream ost ;
ost < < " MidiOutAlsa::openPort: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : INVALID_PARAMETER , errorString_ ) ;
return ;
}
snd_seq_addr_t sender , receiver ;
receiver . client = snd_seq_port_info_get_client ( pinfo ) ;
receiver . port = snd_seq_port_info_get_port ( pinfo ) ;
sender . client = snd_seq_client_id ( data - > seq ) ;
if ( data - > vport < 0 ) {
data - > vport = snd_seq_create_simple_port ( data - > seq , portName . c_str ( ) ,
SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ ,
SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ) ;
if ( data - > vport < 0 ) {
errorString_ = " MidiOutAlsa::openPort: ALSA error creating output port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
}
sender . port = data - > vport ;
// Make subscription
if ( snd_seq_port_subscribe_malloc ( & data - > subscription ) < 0 ) {
snd_seq_port_subscribe_free ( data - > subscription ) ;
errorString_ = " MidiOutAlsa::openPort: error allocating port subscription. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
snd_seq_port_subscribe_set_sender ( data - > subscription , & sender ) ;
snd_seq_port_subscribe_set_dest ( data - > subscription , & receiver ) ;
snd_seq_port_subscribe_set_time_update ( data - > subscription , 1 ) ;
snd_seq_port_subscribe_set_time_real ( data - > subscription , 1 ) ;
if ( snd_seq_subscribe_port ( data - > seq , data - > subscription ) ) {
snd_seq_port_subscribe_free ( data - > subscription ) ;
errorString_ = " MidiOutAlsa::openPort: ALSA error making port connection. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
connected_ = true ;
}
void MidiOutAlsa : : closePort ( void )
{
if ( connected_ ) {
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
snd_seq_unsubscribe_port ( data - > seq , data - > subscription ) ;
snd_seq_port_subscribe_free ( data - > subscription ) ;
data - > subscription = 0 ;
connected_ = false ;
}
}
void MidiOutAlsa : : setClientName ( const std : : string & clientName )
{
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
snd_seq_set_client_name ( data - > seq , clientName . c_str ( ) ) ;
}
void MidiOutAlsa : : setPortName ( const std : : string & portName )
{
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
snd_seq_get_port_info ( data - > seq , data - > vport , pinfo ) ;
snd_seq_port_info_set_name ( pinfo , portName . c_str ( ) ) ;
snd_seq_set_port_info ( data - > seq , data - > vport , pinfo ) ;
}
void MidiOutAlsa : : openVirtualPort ( const std : : string & portName )
{
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
if ( data - > vport < 0 ) {
data - > vport = snd_seq_create_simple_port ( data - > seq , portName . c_str ( ) ,
SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ ,
SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ) ;
if ( data - > vport < 0 ) {
errorString_ = " MidiOutAlsa::openVirtualPort: ALSA error creating virtual port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
}
}
}
void MidiOutAlsa : : sendMessage ( const unsigned char * message , size_t size )
{
long result ;
AlsaMidiData * data = static_cast < AlsaMidiData * > ( apiData_ ) ;
unsigned int nBytes = static_cast < unsigned int > ( size ) ;
if ( nBytes > data - > bufferSize ) {
data - > bufferSize = nBytes ;
result = snd_midi_event_resize_buffer ( data - > coder , nBytes ) ;
if ( result ! = 0 ) {
errorString_ = " MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
free ( data - > buffer ) ;
data - > buffer = ( unsigned char * ) malloc ( data - > bufferSize ) ;
if ( data - > buffer = = NULL ) {
errorString_ = " MidiOutAlsa::initialize: error allocating buffer memory! \n \n " ;
error ( RtMidiError : : MEMORY_ERROR , errorString_ ) ;
return ;
}
}
for ( unsigned int i = 0 ; i < nBytes ; + + i ) data - > buffer [ i ] = message [ i ] ;
unsigned int offset = 0 ;
while ( offset < nBytes ) {
snd_seq_event_t ev ;
snd_seq_ev_clear ( & ev ) ;
snd_seq_ev_set_source ( & ev , data - > vport ) ;
snd_seq_ev_set_subs ( & ev ) ;
snd_seq_ev_set_direct ( & ev ) ;
result = snd_midi_event_encode ( data - > coder , data - > buffer + offset ,
( long ) ( nBytes - offset ) , & ev ) ;
if ( result < 0 ) {
errorString_ = " MidiOutAlsa::sendMessage: event parsing error! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
if ( ev . type = = SND_SEQ_EVENT_NONE ) {
errorString_ = " MidiOutAlsa::sendMessage: incomplete message! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
offset + = result ;
// Send the event.
result = snd_seq_event_output ( data - > seq , & ev ) ;
if ( result < 0 ) {
errorString_ = " MidiOutAlsa::sendMessage: error sending MIDI message to port. " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
}
snd_seq_drain_output ( data - > seq ) ;
}
# endif // __LINUX_ALSA__
//*********************************************************************//
// API: Windows Multimedia Library (MM)
//*********************************************************************//
// API information deciphered from:
// - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
// Thanks to Jean-Baptiste Berruchon for the sysex code.
# if defined(__WINDOWS_MM__)
// The Windows MM API is based on the use of a callback function for
// MIDI input. We convert the system specific time stamps to delta
// time values.
// Windows MM MIDI header files.
# include <windows.h>
# include <mmsystem.h>
// Convert a null-terminated wide string or ANSI-encoded string to UTF-8.
static std : : string ConvertToUTF8 ( const TCHAR * str )
{
std : : string u8str ;
const WCHAR * wstr = L " " ;
# if defined( UNICODE ) || defined( _UNICODE )
wstr = str ;
# else
// Convert from ANSI encoding to wide string
int wlength = MultiByteToWideChar ( CP_ACP , 0 , str , - 1 , NULL , 0 ) ;
std : : wstring wstrtemp ;
if ( wlength )
{
wstrtemp . assign ( wlength - 1 , 0 ) ;
MultiByteToWideChar ( CP_ACP , 0 , str , - 1 , & wstrtemp [ 0 ] , wlength ) ;
wstr = & wstrtemp [ 0 ] ;
}
# endif
// Convert from wide string to UTF-8
int length = WideCharToMultiByte ( CP_UTF8 , 0 , wstr , - 1 , NULL , 0 , NULL , NULL ) ;
if ( length )
{
u8str . assign ( length - 1 , 0 ) ;
length = WideCharToMultiByte ( CP_UTF8 , 0 , wstr , - 1 , & u8str [ 0 ] , length , NULL , NULL ) ;
}
return u8str ;
}
// A structure to hold variables related to the CoreMIDI API
// implementation.
struct WinMidiData {
HMIDIIN inHandle ; // Handle to Midi Input Device
HMIDIOUT outHandle ; // Handle to Midi Output Device
DWORD lastTime ;
MidiInApi : : MidiMessage message ;
std : : vector < LPMIDIHDR > sysexBuffer ;
CRITICAL_SECTION _mutex ; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
} ;
//*********************************************************************//
// API: Windows MM
// Class Definitions: MidiInWinMM
//*********************************************************************//
static void CALLBACK midiInputCallback ( HMIDIIN /*hmin*/ ,
UINT inputStatus ,
DWORD_PTR instancePtr ,
DWORD_PTR midiMessage ,
DWORD timestamp )
{
if ( inputStatus ! = MIM_DATA & & inputStatus ! = MIM_LONGDATA & & inputStatus ! = MIM_LONGERROR ) return ;
//MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr);
MidiInApi : : RtMidiInData * data = ( MidiInApi : : RtMidiInData * ) instancePtr ;
WinMidiData * apiData = static_cast < WinMidiData * > ( data - > apiData ) ;
// Calculate time stamp.
if ( data - > firstMessage = = true ) {
apiData - > message . timeStamp = 0.0 ;
data - > firstMessage = false ;
}
else apiData - > message . timeStamp = ( double ) ( timestamp - apiData - > lastTime ) * 0.001 ;
if ( inputStatus = = MIM_DATA ) { // Channel or system message
// Make sure the first byte is a status byte.
unsigned char status = ( unsigned char ) ( midiMessage & 0x000000FF ) ;
if ( ! ( status & 0x80 ) ) return ;
// Determine the number of bytes in the MIDI message.
unsigned short nBytes = 1 ;
if ( status < 0xC0 ) nBytes = 3 ;
else if ( status < 0xE0 ) nBytes = 2 ;
else if ( status < 0xF0 ) nBytes = 3 ;
else if ( status = = 0xF1 ) {
if ( data - > ignoreFlags & 0x02 ) return ;
else nBytes = 2 ;
}
else if ( status = = 0xF2 ) nBytes = 3 ;
else if ( status = = 0xF3 ) nBytes = 2 ;
else if ( status = = 0xF8 & & ( data - > ignoreFlags & 0x02 ) ) {
// A MIDI timing tick message and we're ignoring it.
return ;
}
else if ( status = = 0xFE & & ( data - > ignoreFlags & 0x04 ) ) {
// A MIDI active sensing message and we're ignoring it.
return ;
}
// Copy bytes to our MIDI message.
unsigned char * ptr = ( unsigned char * ) & midiMessage ;
for ( int i = 0 ; i < nBytes ; + + i ) apiData - > message . bytes . push_back ( * ptr + + ) ;
}
else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )
MIDIHDR * sysex = ( MIDIHDR * ) midiMessage ;
if ( ! ( data - > ignoreFlags & 0x01 ) & & inputStatus ! = MIM_LONGERROR ) {
// Sysex message and we're not ignoring it
for ( int i = 0 ; i < ( int ) sysex - > dwBytesRecorded ; + + i )
apiData - > message . bytes . push_back ( sysex - > lpData [ i ] ) ;
}
// The WinMM API requires that the sysex buffer be requeued after
// input of each sysex message. Even if we are ignoring sysex
// messages, we still need to requeue the buffer in case the user
// decides to not ignore sysex messages in the future. However,
// it seems that WinMM calls this function with an empty sysex
// buffer when an application closes and in this case, we should
// avoid requeueing it, else the computer suddenly reboots after
// one or two minutes.
if ( apiData - > sysexBuffer [ sysex - > dwUser ] - > dwBytesRecorded > 0 ) {
//if ( sysex->dwBytesRecorded > 0 ) {
EnterCriticalSection ( & ( apiData - > _mutex ) ) ;
MMRESULT result = midiInAddBuffer ( apiData - > inHandle , apiData - > sysexBuffer [ sysex - > dwUser ] , sizeof ( MIDIHDR ) ) ;
LeaveCriticalSection ( & ( apiData - > _mutex ) ) ;
if ( result ! = MMSYSERR_NOERROR )
std : : cerr < < " \n RtMidiIn::midiInputCallback: error sending sysex to Midi device!! \n \n " ;
if ( data - > ignoreFlags & 0x01 ) return ;
}
else return ;
}
// Save the time of the last non-filtered message
apiData - > lastTime = timestamp ;
if ( data - > usingCallback ) {
RtMidiIn : : RtMidiCallback callback = ( RtMidiIn : : RtMidiCallback ) data - > userCallback ;
callback ( apiData - > message . timeStamp , & apiData - > message . bytes , data - > userData ) ;
}
else {
// As long as we haven't reached our queue size limit, push the message.
if ( ! data - > queue . push ( apiData - > message ) )
std : : cerr < < " \n MidiInWinMM: message queue limit reached!! \n \n " ;
}
// Clear the vector for the next input message.
apiData - > message . bytes . clear ( ) ;
}
MidiInWinMM : : MidiInWinMM ( const std : : string & clientName , unsigned int queueSizeLimit )
: MidiInApi ( queueSizeLimit )
{
MidiInWinMM : : initialize ( clientName ) ;
}
MidiInWinMM : : ~ MidiInWinMM ( )
{
// Close a connection if it exists.
MidiInWinMM : : closePort ( ) ;
WinMidiData * data = static_cast < WinMidiData * > ( apiData_ ) ;
DeleteCriticalSection ( & ( data - > _mutex ) ) ;
// Cleanup.
delete data ;
}
void MidiInWinMM : : initialize ( const std : : string & /*clientName*/ )
{
// We'll issue a warning here if no devices are available but not
// throw an error since the user can plugin something later.
unsigned int nDevices = midiInGetNumDevs ( ) ;
if ( nDevices = = 0 ) {
errorString_ = " MidiInWinMM::initialize: no MIDI input devices currently available. " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
// Save our api-specific connection information.
WinMidiData * data = ( WinMidiData * ) new WinMidiData ;
apiData_ = ( void * ) data ;
inputData_ . apiData = ( void * ) data ;
data - > message . bytes . clear ( ) ; // needs to be empty for first input message
if ( ! InitializeCriticalSectionAndSpinCount ( & ( data - > _mutex ) , 0x00000400 ) ) {
errorString_ = " MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed. " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
}
void MidiInWinMM : : openPort ( unsigned int portNumber , const std : : string & /*portName*/ )
{
if ( connected_ ) {
errorString_ = " MidiInWinMM::openPort: a valid connection already exists! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
unsigned int nDevices = midiInGetNumDevs ( ) ;
if ( nDevices = = 0 ) {
errorString_ = " MidiInWinMM::openPort: no MIDI input sources found! " ;
error ( RtMidiError : : NO_DEVICES_FOUND , errorString_ ) ;
return ;
}
if ( portNumber > = nDevices ) {
std : : ostringstream ost ;
ost < < " MidiInWinMM::openPort: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : INVALID_PARAMETER , errorString_ ) ;
return ;
}
WinMidiData * data = static_cast < WinMidiData * > ( apiData_ ) ;
MMRESULT result = midiInOpen ( & data - > inHandle ,
portNumber ,
( DWORD_PTR ) & midiInputCallback ,
( DWORD_PTR ) & inputData_ ,
CALLBACK_FUNCTION ) ;
if ( result ! = MMSYSERR_NOERROR ) {
errorString_ = " MidiInWinMM::openPort: error creating Windows MM MIDI input port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Allocate and init the sysex buffers.
data - > sysexBuffer . resize ( inputData_ . bufferCount ) ;
2022-02-17 09:33:19 +00:00
for ( unsigned int i = 0 ; i < inputData_ . bufferCount ; + + i ) {
2022-02-13 20:02:43 +00:00
data - > sysexBuffer [ i ] = ( MIDIHDR * ) new char [ sizeof ( MIDIHDR ) ] ;
data - > sysexBuffer [ i ] - > lpData = new char [ inputData_ . bufferSize ] ;
data - > sysexBuffer [ i ] - > dwBufferLength = inputData_ . bufferSize ;
data - > sysexBuffer [ i ] - > dwUser = i ; // We use the dwUser parameter as buffer indicator
data - > sysexBuffer [ i ] - > dwFlags = 0 ;
result = midiInPrepareHeader ( data - > inHandle , data - > sysexBuffer [ i ] , sizeof ( MIDIHDR ) ) ;
if ( result ! = MMSYSERR_NOERROR ) {
midiInClose ( data - > inHandle ) ;
data - > inHandle = 0 ;
errorString_ = " MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader). " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Register the buffer.
result = midiInAddBuffer ( data - > inHandle , data - > sysexBuffer [ i ] , sizeof ( MIDIHDR ) ) ;
if ( result ! = MMSYSERR_NOERROR ) {
midiInClose ( data - > inHandle ) ;
data - > inHandle = 0 ;
errorString_ = " MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer). " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
}
result = midiInStart ( data - > inHandle ) ;
if ( result ! = MMSYSERR_NOERROR ) {
midiInClose ( data - > inHandle ) ;
data - > inHandle = 0 ;
errorString_ = " MidiInWinMM::openPort: error starting Windows MM MIDI input port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
connected_ = true ;
}
void MidiInWinMM : : openVirtualPort ( const std : : string & /*portName*/ )
{
// This function cannot be implemented for the Windows MM MIDI API.
errorString_ = " MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiInWinMM : : closePort ( void )
{
if ( connected_ ) {
WinMidiData * data = static_cast < WinMidiData * > ( apiData_ ) ;
EnterCriticalSection ( & ( data - > _mutex ) ) ;
midiInReset ( data - > inHandle ) ;
midiInStop ( data - > inHandle ) ;
2022-02-17 09:33:19 +00:00
for ( size_t i = 0 ; i < data - > sysexBuffer . size ( ) ; + + i ) {
2022-02-13 20:02:43 +00:00
int result = midiInUnprepareHeader ( data - > inHandle , data - > sysexBuffer [ i ] , sizeof ( MIDIHDR ) ) ;
delete [ ] data - > sysexBuffer [ i ] - > lpData ;
delete [ ] data - > sysexBuffer [ i ] ;
if ( result ! = MMSYSERR_NOERROR ) {
midiInClose ( data - > inHandle ) ;
data - > inHandle = 0 ;
errorString_ = " MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader). " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
}
midiInClose ( data - > inHandle ) ;
data - > inHandle = 0 ;
connected_ = false ;
LeaveCriticalSection ( & ( data - > _mutex ) ) ;
}
}
void MidiInWinMM : : setClientName ( const std : : string & )
{
errorString_ = " MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiInWinMM : : setPortName ( const std : : string & )
{
errorString_ = " MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
unsigned int MidiInWinMM : : getPortCount ( )
{
return midiInGetNumDevs ( ) ;
}
std : : string MidiInWinMM : : getPortName ( unsigned int portNumber )
{
std : : string stringName ;
unsigned int nDevices = midiInGetNumDevs ( ) ;
if ( portNumber > = nDevices ) {
std : : ostringstream ost ;
ost < < " MidiInWinMM::getPortName: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return stringName ;
}
MIDIINCAPS deviceCaps ;
midiInGetDevCaps ( portNumber , & deviceCaps , sizeof ( MIDIINCAPS ) ) ;
stringName = ConvertToUTF8 ( deviceCaps . szPname ) ;
// Next lines added to add the portNumber to the name so that
// the device's names are sure to be listed with individual names
// even when they have the same brand name
# ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
std : : ostringstream os ;
os < < " " ;
os < < portNumber ;
stringName + = os . str ( ) ;
# endif
return stringName ;
}
//*********************************************************************//
// API: Windows MM
// Class Definitions: MidiOutWinMM
//*********************************************************************//
MidiOutWinMM : : MidiOutWinMM ( const std : : string & clientName ) : MidiOutApi ( )
{
MidiOutWinMM : : initialize ( clientName ) ;
}
MidiOutWinMM : : ~ MidiOutWinMM ( )
{
// Close a connection if it exists.
MidiOutWinMM : : closePort ( ) ;
// Cleanup.
WinMidiData * data = static_cast < WinMidiData * > ( apiData_ ) ;
delete data ;
}
void MidiOutWinMM : : initialize ( const std : : string & /*clientName*/ )
{
// We'll issue a warning here if no devices are available but not
// throw an error since the user can plug something in later.
unsigned int nDevices = midiOutGetNumDevs ( ) ;
if ( nDevices = = 0 ) {
errorString_ = " MidiOutWinMM::initialize: no MIDI output devices currently available. " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
// Save our api-specific connection information.
WinMidiData * data = ( WinMidiData * ) new WinMidiData ;
apiData_ = ( void * ) data ;
}
unsigned int MidiOutWinMM : : getPortCount ( )
{
return midiOutGetNumDevs ( ) ;
}
std : : string MidiOutWinMM : : getPortName ( unsigned int portNumber )
{
std : : string stringName ;
unsigned int nDevices = midiOutGetNumDevs ( ) ;
if ( portNumber > = nDevices ) {
std : : ostringstream ost ;
ost < < " MidiOutWinMM::getPortName: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return stringName ;
}
MIDIOUTCAPS deviceCaps ;
midiOutGetDevCaps ( portNumber , & deviceCaps , sizeof ( MIDIOUTCAPS ) ) ;
stringName = ConvertToUTF8 ( deviceCaps . szPname ) ;
// Next lines added to add the portNumber to the name so that
// the device's names are sure to be listed with individual names
// even when they have the same brand name
std : : ostringstream os ;
# ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES
os < < " " ;
os < < portNumber ;
stringName + = os . str ( ) ;
# endif
return stringName ;
}
void MidiOutWinMM : : openPort ( unsigned int portNumber , const std : : string & /*portName*/ )
{
if ( connected_ ) {
errorString_ = " MidiOutWinMM::openPort: a valid connection already exists! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
unsigned int nDevices = midiOutGetNumDevs ( ) ;
if ( nDevices < 1 ) {
errorString_ = " MidiOutWinMM::openPort: no MIDI output destinations found! " ;
error ( RtMidiError : : NO_DEVICES_FOUND , errorString_ ) ;
return ;
}
if ( portNumber > = nDevices ) {
std : : ostringstream ost ;
ost < < " MidiOutWinMM::openPort: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : INVALID_PARAMETER , errorString_ ) ;
return ;
}
WinMidiData * data = static_cast < WinMidiData * > ( apiData_ ) ;
MMRESULT result = midiOutOpen ( & data - > outHandle ,
portNumber ,
( DWORD ) NULL ,
( DWORD ) NULL ,
CALLBACK_NULL ) ;
if ( result ! = MMSYSERR_NOERROR ) {
errorString_ = " MidiOutWinMM::openPort: error creating Windows MM MIDI output port. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
connected_ = true ;
}
void MidiOutWinMM : : closePort ( void )
{
if ( connected_ ) {
WinMidiData * data = static_cast < WinMidiData * > ( apiData_ ) ;
// Disabled because midiOutReset triggers 0x7b (if any note was ON) and 0x79 "Reset All
// Controllers" (to all 16 channels) CC messages which is undesirable (see issue #222)
// midiOutReset( data->outHandle );
midiOutClose ( data - > outHandle ) ;
data - > outHandle = 0 ;
connected_ = false ;
}
}
void MidiOutWinMM : : setClientName ( const std : : string & )
{
errorString_ = " MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiOutWinMM : : setPortName ( const std : : string & )
{
errorString_ = " MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiOutWinMM : : openVirtualPort ( const std : : string & /*portName*/ )
{
// This function cannot be implemented for the Windows MM MIDI API.
errorString_ = " MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiOutWinMM : : sendMessage ( const unsigned char * message , size_t size )
{
if ( ! connected_ ) return ;
unsigned int nBytes = static_cast < unsigned int > ( size ) ;
if ( nBytes = = 0 ) {
errorString_ = " MidiOutWinMM::sendMessage: message argument is empty! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
MMRESULT result ;
WinMidiData * data = static_cast < WinMidiData * > ( apiData_ ) ;
if ( message [ 0 ] = = 0xF0 ) { // Sysex message
// Allocate buffer for sysex data.
char * buffer = ( char * ) malloc ( nBytes ) ;
if ( buffer = = NULL ) {
errorString_ = " MidiOutWinMM::sendMessage: error allocating sysex message memory! " ;
error ( RtMidiError : : MEMORY_ERROR , errorString_ ) ;
return ;
}
// Copy data to buffer.
for ( unsigned int i = 0 ; i < nBytes ; + + i ) buffer [ i ] = message [ i ] ;
// Create and prepare MIDIHDR structure.
MIDIHDR sysex ;
sysex . lpData = ( LPSTR ) buffer ;
sysex . dwBufferLength = nBytes ;
sysex . dwFlags = 0 ;
result = midiOutPrepareHeader ( data - > outHandle , & sysex , sizeof ( MIDIHDR ) ) ;
if ( result ! = MMSYSERR_NOERROR ) {
free ( buffer ) ;
errorString_ = " MidiOutWinMM::sendMessage: error preparing sysex header. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Send the message.
result = midiOutLongMsg ( data - > outHandle , & sysex , sizeof ( MIDIHDR ) ) ;
if ( result ! = MMSYSERR_NOERROR ) {
free ( buffer ) ;
errorString_ = " MidiOutWinMM::sendMessage: error sending sysex message. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Unprepare the buffer and MIDIHDR.
while ( MIDIERR_STILLPLAYING = = midiOutUnprepareHeader ( data - > outHandle , & sysex , sizeof ( MIDIHDR ) ) ) Sleep ( 1 ) ;
free ( buffer ) ;
}
else { // Channel or system message.
// Make sure the message size isn't too big.
if ( nBytes > 3 ) {
errorString_ = " MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
// Pack MIDI bytes into double word.
DWORD packet ;
unsigned char * ptr = ( unsigned char * ) & packet ;
for ( unsigned int i = 0 ; i < nBytes ; + + i ) {
* ptr = message [ i ] ;
+ + ptr ;
}
// Send the message immediately.
result = midiOutShortMsg ( data - > outHandle , packet ) ;
if ( result ! = MMSYSERR_NOERROR ) {
errorString_ = " MidiOutWinMM::sendMessage: error sending MIDI message. " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
}
}
}
# endif // __WINDOWS_MM__
//*********************************************************************//
// API: UNIX JACK
//
// Written primarily by Alexander Svetalkin, with updates for delta
// time by Gary Scavone, April 2011.
//
// *********************************************************************//
# if defined(__UNIX_JACK__)
// JACK header files
# include <jack/jack.h>
# include <jack/midiport.h>
# include <jack/ringbuffer.h>
# include <pthread.h>
# ifdef HAVE_SEMAPHORE
# include <semaphore.h>
# endif
# define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
struct JackMidiData {
jack_client_t * client ;
jack_port_t * port ;
jack_ringbuffer_t * buff ;
int buffMaxWrite ; // actual writable size, usually 1 less than ringbuffer
jack_time_t lastTime ;
# ifdef HAVE_SEMAPHORE
sem_t sem_cleanup ;
sem_t sem_needpost ;
# endif
MidiInApi : : RtMidiInData * rtMidiIn ;
} ;
//*********************************************************************//
// API: JACK
// Class Definitions: MidiInJack
//*********************************************************************//
static int jackProcessIn ( jack_nframes_t nframes , void * arg )
{
JackMidiData * jData = ( JackMidiData * ) arg ;
MidiInApi : : RtMidiInData * rtData = jData - > rtMidiIn ;
jack_midi_event_t event ;
jack_time_t time ;
// Is port created?
if ( jData - > port = = NULL ) return 0 ;
void * buff = jack_port_get_buffer ( jData - > port , nframes ) ;
bool & continueSysex = rtData - > continueSysex ;
unsigned char & ignoreFlags = rtData - > ignoreFlags ;
// We have midi events in buffer
int evCount = jack_midi_get_event_count ( buff ) ;
for ( int j = 0 ; j < evCount ; j + + ) {
MidiInApi : : MidiMessage & message = rtData - > message ;
jack_midi_event_get ( & event , buff , j ) ;
// Compute the delta time.
time = jack_get_time ( ) ;
if ( rtData - > firstMessage = = true ) {
message . timeStamp = 0.0 ;
rtData - > firstMessage = false ;
} else
message . timeStamp = ( time - jData - > lastTime ) * 0.000001 ;
jData - > lastTime = time ;
if ( ! continueSysex )
message . bytes . clear ( ) ;
if ( ! ( ( continueSysex | | event . buffer [ 0 ] = = 0xF0 ) & & ( ignoreFlags & 0x01 ) ) ) {
// Unless this is a (possibly continued) SysEx message and we're ignoring SysEx,
// copy the event buffer into the MIDI message struct.
for ( unsigned int i = 0 ; i < event . size ; i + + )
message . bytes . push_back ( event . buffer [ i ] ) ;
}
switch ( event . buffer [ 0 ] ) {
case 0xF0 :
// Start of a SysEx message
continueSysex = event . buffer [ event . size - 1 ] ! = 0xF7 ;
if ( ignoreFlags & 0x01 ) continue ;
break ;
case 0xF1 :
case 0xF8 :
// MIDI Time Code or Timing Clock message
if ( ignoreFlags & 0x02 ) continue ;
break ;
case 0xFE :
// Active Sensing message
if ( ignoreFlags & 0x04 ) continue ;
break ;
default :
if ( continueSysex ) {
// Continuation of a SysEx message
continueSysex = event . buffer [ event . size - 1 ] ! = 0xF7 ;
if ( ignoreFlags & 0x01 ) continue ;
}
// All other MIDI messages
}
if ( ! continueSysex ) {
// If not a continuation of a SysEx message,
// invoke the user callback function or queue the message.
if ( rtData - > usingCallback ) {
RtMidiIn : : RtMidiCallback callback = ( RtMidiIn : : RtMidiCallback ) rtData - > userCallback ;
callback ( message . timeStamp , & message . bytes , rtData - > userData ) ;
}
else {
// As long as we haven't reached our queue size limit, push the message.
if ( ! rtData - > queue . push ( message ) )
std : : cerr < < " \n MidiInJack: message queue limit reached!! \n \n " ;
}
}
}
return 0 ;
}
MidiInJack : : MidiInJack ( const std : : string & clientName , unsigned int queueSizeLimit )
: MidiInApi ( queueSizeLimit )
{
MidiInJack : : initialize ( clientName ) ;
}
void MidiInJack : : initialize ( const std : : string & clientName )
{
JackMidiData * data = new JackMidiData ;
apiData_ = ( void * ) data ;
data - > rtMidiIn = & inputData_ ;
data - > port = NULL ;
data - > client = NULL ;
this - > clientName = clientName ;
connect ( ) ;
}
void MidiInJack : : connect ( )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
if ( data - > client )
return ;
// Initialize JACK client
if ( ( data - > client = jack_client_open ( clientName . c_str ( ) , JackNoStartServer , NULL ) ) = = 0 ) {
errorString_ = " MidiInJack::initialize: JACK server not running? " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
jack_set_process_callback ( data - > client , jackProcessIn , data ) ;
jack_activate ( data - > client ) ;
}
MidiInJack : : ~ MidiInJack ( )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
MidiInJack : : closePort ( ) ;
if ( data - > client )
jack_client_close ( data - > client ) ;
delete data ;
}
void MidiInJack : : openPort ( unsigned int portNumber , const std : : string & portName )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
connect ( ) ;
// Creating new port
if ( data - > port = = NULL )
data - > port = jack_port_register ( data - > client , portName . c_str ( ) ,
JACK_DEFAULT_MIDI_TYPE , JackPortIsInput , 0 ) ;
if ( data - > port = = NULL ) {
errorString_ = " MidiInJack::openPort: JACK error creating port " ;
if ( portName . size ( ) > = ( size_t ) jack_port_name_size ( ) )
errorString_ + = " (port name too long?) " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Connecting to the output
std : : string name = getPortName ( portNumber ) ;
jack_connect ( data - > client , name . c_str ( ) , jack_port_name ( data - > port ) ) ;
connected_ = true ;
}
void MidiInJack : : openVirtualPort ( const std : : string & portName )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
connect ( ) ;
if ( data - > port = = NULL )
data - > port = jack_port_register ( data - > client , portName . c_str ( ) ,
JACK_DEFAULT_MIDI_TYPE , JackPortIsInput , 0 ) ;
if ( data - > port = = NULL ) {
errorString_ = " MidiInJack::openVirtualPort: JACK error creating virtual port " ;
if ( portName . size ( ) > = ( size_t ) jack_port_name_size ( ) )
errorString_ + = " (port name too long?) " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
}
}
unsigned int MidiInJack : : getPortCount ( )
{
int count = 0 ;
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
connect ( ) ;
if ( ! data - > client )
return 0 ;
// List of available ports
const char * * ports = jack_get_ports ( data - > client , NULL , JACK_DEFAULT_MIDI_TYPE , JackPortIsOutput ) ;
if ( ports = = NULL ) return 0 ;
while ( ports [ count ] ! = NULL )
count + + ;
free ( ports ) ;
return count ;
}
std : : string MidiInJack : : getPortName ( unsigned int portNumber )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
std : : string retStr ( " " ) ;
connect ( ) ;
// List of available ports
const char * * ports = jack_get_ports ( data - > client , NULL ,
JACK_DEFAULT_MIDI_TYPE , JackPortIsOutput ) ;
// Check port validity
if ( ports = = NULL ) {
errorString_ = " MidiInJack::getPortName: no ports available! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return retStr ;
}
unsigned int i ;
for ( i = 0 ; i < portNumber & & ports [ i ] ; i + + ) { }
if ( i < portNumber | | ! ports [ portNumber ] ) {
std : : ostringstream ost ;
ost < < " MidiInJack::getPortName: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
else retStr . assign ( ports [ portNumber ] ) ;
jack_free ( ports ) ;
return retStr ;
}
void MidiInJack : : closePort ( )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
if ( data - > port = = NULL ) return ;
jack_port_unregister ( data - > client , data - > port ) ;
data - > port = NULL ;
connected_ = false ;
}
void MidiInJack : : setClientName ( const std : : string & )
{
errorString_ = " MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiInJack : : setPortName ( const std : : string & portName )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
# ifdef JACK_HAS_PORT_RENAME
jack_port_rename ( data - > client , data - > port , portName . c_str ( ) ) ;
# else
jack_port_set_name ( data - > port , portName . c_str ( ) ) ;
# endif
}
//*********************************************************************//
// API: JACK
// Class Definitions: MidiOutJack
//*********************************************************************//
// Jack process callback
static int jackProcessOut ( jack_nframes_t nframes , void * arg )
{
JackMidiData * data = ( JackMidiData * ) arg ;
jack_midi_data_t * midiData ;
int space ;
// Is port created?
if ( data - > port = = NULL ) return 0 ;
void * buff = jack_port_get_buffer ( data - > port , nframes ) ;
jack_midi_clear_buffer ( buff ) ;
while ( jack_ringbuffer_peek ( data - > buff , ( char * ) & space , sizeof ( space ) ) = = sizeof ( space ) & &
jack_ringbuffer_read_space ( data - > buff ) > = sizeof ( space ) + space ) {
jack_ringbuffer_read_advance ( data - > buff , sizeof ( space ) ) ;
midiData = jack_midi_event_reserve ( buff , 0 , space ) ;
if ( midiData )
jack_ringbuffer_read ( data - > buff , ( char * ) midiData , ( size_t ) space ) ;
else
jack_ringbuffer_read_advance ( data - > buff , ( size_t ) space ) ;
}
# ifdef HAVE_SEMAPHORE
if ( ! sem_trywait ( & data - > sem_needpost ) )
sem_post ( & data - > sem_cleanup ) ;
# endif
return 0 ;
}
MidiOutJack : : MidiOutJack ( const std : : string & clientName ) : MidiOutApi ( )
{
MidiOutJack : : initialize ( clientName ) ;
}
void MidiOutJack : : initialize ( const std : : string & clientName )
{
JackMidiData * data = new JackMidiData ;
apiData_ = ( void * ) data ;
data - > port = NULL ;
data - > client = NULL ;
# ifdef HAVE_SEMAPHORE
sem_init ( & data - > sem_cleanup , 0 , 0 ) ;
sem_init ( & data - > sem_needpost , 0 , 0 ) ;
# endif
this - > clientName = clientName ;
connect ( ) ;
}
void MidiOutJack : : connect ( )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
if ( data - > client )
return ;
// Initialize output ringbuffers
data - > buff = jack_ringbuffer_create ( JACK_RINGBUFFER_SIZE ) ;
data - > buffMaxWrite = ( int ) jack_ringbuffer_write_space ( data - > buff ) ;
// Initialize JACK client
if ( ( data - > client = jack_client_open ( clientName . c_str ( ) , JackNoStartServer , NULL ) ) = = 0 ) {
errorString_ = " MidiOutJack::initialize: JACK server not running? " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return ;
}
jack_set_process_callback ( data - > client , jackProcessOut , data ) ;
jack_activate ( data - > client ) ;
}
MidiOutJack : : ~ MidiOutJack ( )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
MidiOutJack : : closePort ( ) ;
// Cleanup
jack_ringbuffer_free ( data - > buff ) ;
if ( data - > client ) {
jack_client_close ( data - > client ) ;
}
# ifdef HAVE_SEMAPHORE
sem_destroy ( & data - > sem_cleanup ) ;
sem_destroy ( & data - > sem_needpost ) ;
# endif
delete data ;
}
void MidiOutJack : : openPort ( unsigned int portNumber , const std : : string & portName )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
connect ( ) ;
// Creating new port
if ( data - > port = = NULL )
data - > port = jack_port_register ( data - > client , portName . c_str ( ) ,
JACK_DEFAULT_MIDI_TYPE , JackPortIsOutput , 0 ) ;
if ( data - > port = = NULL ) {
errorString_ = " MidiOutJack::openPort: JACK error creating port " ;
if ( portName . size ( ) > = ( size_t ) jack_port_name_size ( ) )
errorString_ + = " (port name too long?) " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
return ;
}
// Connecting to the output
std : : string name = getPortName ( portNumber ) ;
jack_connect ( data - > client , jack_port_name ( data - > port ) , name . c_str ( ) ) ;
connected_ = true ;
}
void MidiOutJack : : openVirtualPort ( const std : : string & portName )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
connect ( ) ;
if ( data - > port = = NULL )
data - > port = jack_port_register ( data - > client , portName . c_str ( ) ,
JACK_DEFAULT_MIDI_TYPE , JackPortIsOutput , 0 ) ;
if ( data - > port = = NULL ) {
errorString_ = " MidiOutJack::openVirtualPort: JACK error creating virtual port " ;
if ( portName . size ( ) > = ( size_t ) jack_port_name_size ( ) )
errorString_ + = " (port name too long?) " ;
error ( RtMidiError : : DRIVER_ERROR , errorString_ ) ;
}
}
unsigned int MidiOutJack : : getPortCount ( )
{
int count = 0 ;
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
connect ( ) ;
if ( ! data - > client )
return 0 ;
// List of available ports
const char * * ports = jack_get_ports ( data - > client , NULL ,
JACK_DEFAULT_MIDI_TYPE , JackPortIsInput ) ;
if ( ports = = NULL ) return 0 ;
while ( ports [ count ] ! = NULL )
count + + ;
free ( ports ) ;
return count ;
}
std : : string MidiOutJack : : getPortName ( unsigned int portNumber )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
std : : string retStr ( " " ) ;
connect ( ) ;
// List of available ports
const char * * ports = jack_get_ports ( data - > client , NULL ,
JACK_DEFAULT_MIDI_TYPE , JackPortIsInput ) ;
// Check port validity
if ( ports = = NULL ) {
errorString_ = " MidiOutJack::getPortName: no ports available! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
return retStr ;
}
if ( ports [ portNumber ] = = NULL ) {
std : : ostringstream ost ;
ost < < " MidiOutJack::getPortName: the 'portNumber' argument ( " < < portNumber < < " ) is invalid. " ;
errorString_ = ost . str ( ) ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
else retStr . assign ( ports [ portNumber ] ) ;
free ( ports ) ;
return retStr ;
}
void MidiOutJack : : closePort ( )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
if ( data - > port = = NULL ) return ;
# ifdef HAVE_SEMAPHORE
struct timespec ts ;
if ( clock_gettime ( CLOCK_REALTIME , & ts ) ! = - 1 ) {
ts . tv_sec + = 1 ; // wait max one second
sem_post ( & data - > sem_needpost ) ;
sem_timedwait ( & data - > sem_cleanup , & ts ) ;
}
# endif
jack_port_unregister ( data - > client , data - > port ) ;
data - > port = NULL ;
connected_ = false ;
}
void MidiOutJack : : setClientName ( const std : : string & )
{
errorString_ = " MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiOutJack : : setPortName ( const std : : string & portName )
{
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
# ifdef JACK_HAS_PORT_RENAME
jack_port_rename ( data - > client , data - > port , portName . c_str ( ) ) ;
# else
jack_port_set_name ( data - > port , portName . c_str ( ) ) ;
# endif
}
void MidiOutJack : : sendMessage ( const unsigned char * message , size_t size )
{
int nBytes = static_cast < int > ( size ) ;
JackMidiData * data = static_cast < JackMidiData * > ( apiData_ ) ;
if ( size + sizeof ( nBytes ) > ( size_t ) data - > buffMaxWrite )
return ;
while ( jack_ringbuffer_write_space ( data - > buff ) < sizeof ( nBytes ) + size )
pthread_yield ( ) ;
// Write full message to buffer
jack_ringbuffer_write ( data - > buff , ( char * ) & nBytes , sizeof ( nBytes ) ) ;
jack_ringbuffer_write ( data - > buff , ( const char * ) message , nBytes ) ;
}
# endif // __UNIX_JACK__
//*********************************************************************//
// API: Web MIDI
//
// Written primarily by Atsushi Eno, February 2020.
//
// *********************************************************************//
# if defined(__WEB_MIDI_API__)
# include <emscripten.h>
//*********************************************************************//
// API: WEB MIDI
// Class Definitions: WebMidiAccessShim
//*********************************************************************//
class WebMidiAccessShim
{
public :
WebMidiAccessShim ( ) ;
~ WebMidiAccessShim ( ) ;
std : : string getPortName ( unsigned int portNumber , bool isInput ) ;
} ;
std : : unique_ptr < WebMidiAccessShim > shim { nullptr } ;
void ensureShim ( )
{
if ( shim . get ( ) ! = nullptr )
return ;
shim . reset ( new WebMidiAccessShim ( ) ) ;
}
bool checkWebMidiAvailability ( )
{
ensureShim ( ) ;
return MAIN_THREAD_EM_ASM_INT ( {
if ( typeof window . _rtmidi_internals_waiting = = = " undefined " ) {
console . log ( " Attempted to use Web MIDI API without trying to open it. " ) ;
return false ;
}
if ( window . _rtmidi_internals_waiting ) {
console . log ( " Attempted to use Web MIDI API while it is being queried. " ) ;
return false ;
}
if ( _rtmidi_internals_midi_access = = null ) {
console . log ( " Attempted to use Web MIDI API while it already turned out to be unavailable. " ) ;
return false ;
}
return true ;
} ) ;
}
WebMidiAccessShim : : WebMidiAccessShim ( )
{
MAIN_THREAD_ASYNC_EM_ASM ( {
if ( typeof window . _rtmidi_internals_midi_access ! = = " undefined " )
return ;
if ( typeof window . _rtmidi_internals_waiting ! = = " undefined " ) {
console . log ( " MIDI Access was requested while another request is in progress. " ) ;
return ;
}
// define functions
window . _rtmidi_internals_get_port_by_number = function ( portNumber , isInput ) {
var midi = window . _rtmidi_internals_midi_access ;
var devices = isInput ? midi . inputs : midi . outputs ;
var i = 0 ;
for ( var device of devices . values ( ) ) {
if ( i = = portNumber )
return device ;
i + + ;
}
console . log ( " MIDI " + ( isInput ? " input " : " output " ) + " device of portNumber " + portNumber + " is not found. " ) ;
return null ;
} ;
window . _rtmidi_internals_waiting = true ;
window . navigator . requestMIDIAccess ( { " sysex " : true } ) . then ( ( midiAccess ) = > {
window . _rtmidi_internals_midi_access = midiAccess ;
window . _rtmidi_internals_latest_message_timestamp = 0.0 ;
window . _rtmidi_internals_waiting = false ;
if ( midiAccess = = null ) {
console . log ( " Could not get access to MIDI API " ) ;
}
} ) ;
} ) ;
}
WebMidiAccessShim : : ~ WebMidiAccessShim ( )
{
}
std : : string WebMidiAccessShim : : getPortName ( unsigned int portNumber , bool isInput )
{
if ( ! checkWebMidiAvailability ( ) )
return " " ;
char * ret = ( char * ) MAIN_THREAD_EM_ASM_INT ( {
var port = window . _rtmidi_internals_get_port_by_number ( $ 0 , $ 1 ) ;
if ( port = = null )
return null ;
var length = lengthBytesUTF8 ( port . name ) + 1 ;
var ret = _malloc ( length ) ;
stringToUTF8 ( port . name , ret , length ) ;
return ret ;
} , portNumber , isInput , & ret ) ;
if ( ret = = nullptr )
return " " ;
std : : string s = ret ;
free ( ret ) ;
return s ;
}
//*********************************************************************//
// API: WEB MIDI
// Class Definitions: MidiInWeb
//*********************************************************************//
MidiInWeb : : MidiInWeb ( const std : : string & clientName , unsigned int queueSizeLimit )
: MidiInApi ( queueSizeLimit )
{
initialize ( clientName ) ;
}
MidiInWeb : : ~ MidiInWeb ( void )
{
closePort ( ) ;
}
extern " C " void EMSCRIPTEN_KEEPALIVE rtmidi_onMidiMessageProc ( MidiInApi : : RtMidiInData * data , uint8_t * inputBytes , int32_t length , double domHighResTimeStamp )
{
auto & message = data - > message ;
message . bytes . resize ( message . bytes . size ( ) + length ) ;
memcpy ( message . bytes . data ( ) , inputBytes , length ) ;
// FIXME: handle timestamp
if ( data - > usingCallback ) {
RtMidiIn : : RtMidiCallback callback = ( RtMidiIn : : RtMidiCallback ) data - > userCallback ;
callback ( message . timeStamp , & message . bytes , data - > userData ) ;
}
}
void MidiInWeb : : openPort ( unsigned int portNumber , const std : : string & portName )
{
if ( ! checkWebMidiAvailability ( ) )
return ;
if ( open_port_number > = 0 )
return ;
MAIN_THREAD_EM_ASM ( {
// In Web MIDI API world, there is no step to open a port, but we have to register the input callback instead.
var input = window . _rtmidi_internals_get_port_by_number ( $ 0 , true ) ;
input . onmidimessage = function ( e ) {
// In RtMidi world, timestamps are delta time from previous message, while in Web MIDI world
// timestamps are relative to window creation time (i.e. kind of absolute time with window "epoch" time).
var rtmidiTimestamp = window . _rtmidi_internals_latest_message_timestamp = = 0.0 ? 0.0 : e . timeStamp - window . _rtmidi_internals_latest_message_timestamp ;
window . _rtmidi_internals_latest_message_timestamp = e . timeStamp ;
Module . ccall ( ' rtmidi_onMidiMessageProc ' , ' void ' , [ ' number ' , ' array ' , ' number ' , ' number ' ] , [ $ 1 , e . data , e . data . length , rtmidiTimestamp ] ) ;
} ;
} , portNumber , & inputData_ ) ;
open_port_number = portNumber ;
}
void MidiInWeb : : openVirtualPort ( const std : : string & portName )
{
errorString_ = " MidiInWeb::openVirtualPort: this function is not implemented for the Web MIDI API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiInWeb : : closePort ( void )
{
if ( open_port_number < 0 )
return ;
MAIN_THREAD_EM_ASM ( {
var input = _rtmidi_internals_get_port_by_number ( $ 0 , true ) ;
if ( input = = null ) {
console . log ( " Port # " + $ 0 + " could not be found. " ) ;
return ;
}
// unregister event handler
input . onmidimessage = null ;
} , open_port_number ) ;
open_port_number = - 1 ;
}
void MidiInWeb : : setClientName ( const std : : string & clientName )
{
client_name = clientName ;
}
void MidiInWeb : : setPortName ( const std : : string & portName )
{
errorString_ = " MidiInWeb::setPortName: this function is not implemented for the Web MIDI API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
unsigned int MidiInWeb : : getPortCount ( void )
{
if ( ! checkWebMidiAvailability ( ) )
return 0 ;
return MAIN_THREAD_EM_ASM_INT ( { return _rtmidi_internals_midi_access . inputs . size ; } ) ;
}
std : : string MidiInWeb : : getPortName ( unsigned int portNumber )
{
if ( ! checkWebMidiAvailability ( ) )
return " " ;
return shim - > getPortName ( portNumber , true ) ;
}
void MidiInWeb : : initialize ( const std : : string & clientName )
{
ensureShim ( ) ;
setClientName ( clientName ) ;
}
//*********************************************************************//
// API: WEB MIDI
// Class Definitions: MidiOutWeb
//*********************************************************************//
MidiOutWeb : : MidiOutWeb ( const std : : string & clientName )
{
initialize ( clientName ) ;
}
MidiOutWeb : : ~ MidiOutWeb ( void )
{
closePort ( ) ;
}
void MidiOutWeb : : openPort ( unsigned int portNumber , const std : : string & portName )
{
if ( ! checkWebMidiAvailability ( ) )
return ;
if ( open_port_number > = 0 )
return ;
// In Web MIDI API world, there is no step to open a port.
open_port_number = portNumber ;
}
void MidiOutWeb : : openVirtualPort ( const std : : string & portName )
{
errorString_ = " MidiOutWeb::openVirtualPort: this function is not implemented for the Web MIDI API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
void MidiOutWeb : : closePort ( void )
{
// there is really nothing to do for output at JS side.
open_port_number = - 1 ;
}
void MidiOutWeb : : setClientName ( const std : : string & clientName )
{
client_name = clientName ;
}
void MidiOutWeb : : setPortName ( const std : : string & portName )
{
errorString_ = " MidiOutWeb::setPortName: this function is not implemented for the Web MIDI API! " ;
error ( RtMidiError : : WARNING , errorString_ ) ;
}
unsigned int MidiOutWeb : : getPortCount ( void )
{
if ( ! checkWebMidiAvailability ( ) )
return 0 ;
return MAIN_THREAD_EM_ASM_INT ( { return _rtmidi_internals_midi_access . outputs . size ; } ) ;
}
std : : string MidiOutWeb : : getPortName ( unsigned int portNumber )
{
if ( ! checkWebMidiAvailability ( ) )
return " " ;
return shim - > getPortName ( portNumber , false ) ;
}
void MidiOutWeb : : sendMessage ( const unsigned char * message , size_t size )
{
if ( open_port_number < 0 )
return ;
MAIN_THREAD_EM_ASM ( {
var output = _rtmidi_internals_get_port_by_number ( $ 0 , false ) ;
if ( output = = null ) {
console . log ( " Port # " + $ 0 + " could not be found. " ) ;
return ;
}
var buf = new ArrayBuffer ( $ 2 ) ;
var msg = new Uint8Array ( buf ) ;
msg . set ( new Uint8Array ( Module . HEAPU8 . buffer . slice ( $ 1 , $ 1 + $ 2 ) ) ) ;
output . send ( msg ) ;
} , open_port_number , message , size ) ;
}
void MidiOutWeb : : initialize ( const std : : string & clientName )
{
if ( shim . get ( ) ! = nullptr )
return ;
shim . reset ( new WebMidiAccessShim ( ) ) ;
setClientName ( clientName ) ;
}
# endif // __WEB_MIDI_API__