New versions of RtAudio and RtMidi in preparation for new release.

This commit is contained in:
Gary Scavone
2021-11-16 21:28:04 -05:00
parent 67d573b169
commit 1fd900263b
4 changed files with 1342 additions and 495 deletions

View File

@@ -46,7 +46,7 @@
#ifndef __RTAUDIO_H
#define __RTAUDIO_H
#define RTAUDIO_VERSION "5.1.0"
#define RTAUDIO_VERSION "5.2.0"
#if defined _WIN32 || defined __CYGWIN__
#if defined(RTAUDIO_EXPORT)
@@ -226,12 +226,12 @@ class RTAUDIO_DLL_PUBLIC RtAudioError : public std::runtime_error
UNSPECIFIED, /*!< The default, unspecified error type. */
NO_DEVICES_FOUND, /*!< No devices found on system. */
INVALID_DEVICE, /*!< An invalid device ID was specified. */
MEMORY_ERROR, /*!< An error occured during memory allocation. */
MEMORY_ERROR, /*!< An error occurred during memory allocation. */
INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */
INVALID_USE, /*!< The function was called incorrectly. */
DRIVER_ERROR, /*!< A system driver error occured. */
SYSTEM_ERROR, /*!< A system error occured. */
THREAD_ERROR /*!< A thread error occured. */
DRIVER_ERROR, /*!< A system driver error occurred. */
SYSTEM_ERROR, /*!< A system error occurred. */
THREAD_ERROR /*!< A thread error occurred. */
};
//! The constructor.
@@ -299,30 +299,21 @@ class RTAUDIO_DLL_PUBLIC RtAudio
struct DeviceInfo {
bool probed; /*!< true if the device capabilities were successfully probed. */
std::string name; /*!< Character string device identifier. */
unsigned int outputChannels; /*!< Maximum output channels supported by device. */
unsigned int inputChannels; /*!< Maximum input channels supported by device. */
unsigned int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */
bool isDefaultOutput; /*!< true if this is the default output device. */
bool isDefaultInput; /*!< true if this is the default input device. */
unsigned int outputChannels{}; /*!< Maximum output channels supported by device. */
unsigned int inputChannels{}; /*!< Maximum input channels supported by device. */
unsigned int duplexChannels{}; /*!< Maximum simultaneous input/output channels supported by device. */
bool isDefaultOutput{false}; /*!< true if this is the default output device. */
bool isDefaultInput{false}; /*!< true if this is the default input device. */
std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
unsigned int preferredSampleRate; /*!< Preferred sample rate, e.g. for WASAPI the system sample rate. */
RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */
// Default constructor.
DeviceInfo()
:probed(false), outputChannels(0), inputChannels(0), duplexChannels(0),
isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {}
unsigned int preferredSampleRate{}; /*!< Preferred sample rate, e.g. for WASAPI the system sample rate. */
RtAudioFormat nativeFormats{}; /*!< Bit mask of supported data formats. */
};
//! The structure for specifying input or ouput stream parameters.
//! The structure for specifying input or output stream parameters.
struct StreamParameters {
unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */
unsigned int nChannels; /*!< Number of channels. */
unsigned int firstChannel; /*!< First channel index on device (default = 0). */
// Default constructor.
StreamParameters()
: deviceId(0), nChannels(0), firstChannel(0) {}
unsigned int deviceId{}; /*!< Device index (0 to getDeviceCount() - 1). */
unsigned int nChannels{}; /*!< Number of channels. */
unsigned int firstChannel{}; /*!< First channel index on device (default = 0). */
};
//! The structure for specifying stream options.
@@ -383,14 +374,10 @@ class RTAUDIO_DLL_PUBLIC RtAudio
RtAudio with Jack, each instance must have a unique client name.
*/
struct StreamOptions {
RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */
unsigned int numberOfBuffers; /*!< Number of stream buffers. */
RtAudioStreamFlags flags{}; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */
unsigned int numberOfBuffers{}; /*!< Number of stream buffers. */
std::string streamName; /*!< A stream name (currently used only in Jack). */
int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */
// Default constructor.
StreamOptions()
: flags(0), numberOfBuffers(0), priority(0) {}
int priority{}; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */
};
//! A static function to determine the current RtAudio version.
@@ -527,7 +514,7 @@ class RTAUDIO_DLL_PUBLIC RtAudio
lowest allowable value is used. The actual value used is
returned via the structure argument. The parameter is API dependent.
\param errorCallback A client-defined function that will be invoked
when an error has occured.
when an error has occurred.
*/
void openStream( RtAudio::StreamParameters *outputParameters,
RtAudio::StreamParameters *inputParameters,
@@ -616,7 +603,7 @@ class RTAUDIO_DLL_PUBLIC RtAudio
};
// Operating system dependent thread functionality.
#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)
#if defined(_WIN32) || defined(__CYGWIN__)
#ifndef NOMINMAX
#define NOMINMAX
@@ -628,18 +615,22 @@ class RTAUDIO_DLL_PUBLIC RtAudio
typedef uintptr_t ThreadHandle;
typedef CRITICAL_SECTION StreamMutex;
#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
#else
// Using pthread library for various flavors of unix.
#include <pthread.h>
typedef pthread_t ThreadHandle;
typedef pthread_mutex_t StreamMutex;
#else // Setup for "dummy" behavior
#endif
// Setup for "dummy" behavior if no apis specified.
#if !(defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) \
|| defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) \
|| defined(__LINUX_OSS__) || defined(__MACOSX_CORE__))
#define __RTAUDIO_DUMMY__
typedef int ThreadHandle;
typedef int StreamMutex;
#endif
@@ -647,19 +638,15 @@ class RTAUDIO_DLL_PUBLIC RtAudio
// between the private RtAudio stream structure and global callback
// handling functions.
struct CallbackInfo {
void *object; // Used as a "this" pointer.
ThreadHandle thread;
void *callback;
void *userData;
void *errorCallback;
void *apiInfo; // void pointer for API specific callback information
bool isRunning;
bool doRealtime;
int priority;
// Default constructor.
CallbackInfo()
:object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false), priority(0) {}
void *object{}; // Used as a "this" pointer.
ThreadHandle thread{};
void *callback{};
void *userData{};
void *errorCallback{};
void *apiInfo{}; // void pointer for API specific callback information
bool isRunning{false};
bool doRealtime{false};
int priority{};
};
// **************************************************************** //
@@ -686,9 +673,9 @@ class S24 {
S24() {}
S24& operator = ( const int& i ) {
c3[0] = (i & 0x000000ff);
c3[1] = (i & 0x0000ff00) >> 8;
c3[2] = (i & 0x00ff0000) >> 16;
c3[0] = (unsigned char)(i & 0x000000ff);
c3[1] = (unsigned char)((i & 0x0000ff00) >> 8);
c3[2] = (unsigned char)((i & 0x00ff0000) >> 16);
return *this;
}
@@ -895,20 +882,20 @@ public:
RtApiCore();
~RtApiCore();
RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; }
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
unsigned int getDefaultOutputDevice( void );
unsigned int getDefaultInputDevice( void );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi( void ) override { return RtAudio::MACOSX_CORE; }
unsigned int getDeviceCount( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
unsigned int getDefaultOutputDevice( void ) override;
unsigned int getDefaultInputDevice( void ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
// will most likely produce highly undesirable results!
bool callbackEvent( AudioDeviceID deviceId,
const AudioBufferList *inBufferList,
const AudioBufferList *outBufferList );
@@ -918,7 +905,7 @@ public:
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
RtAudio::StreamOptions *options ) override;
static const char* getErrorCode( OSStatus code );
};
@@ -932,18 +919,18 @@ public:
RtApiJack();
~RtApiJack();
RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; }
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi( void ) override { return RtAudio::UNIX_JACK; }
unsigned int getDeviceCount( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
// will most likely produce highly undesirable results!
bool callbackEvent( unsigned long nframes );
private:
@@ -951,7 +938,7 @@ public:
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
RtAudio::StreamOptions *options ) override;
bool shouldAutoconnect_;
};
@@ -966,18 +953,20 @@ public:
RtApiAsio();
~RtApiAsio();
RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; }
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_ASIO; }
unsigned int getDeviceCount( void ) override;
unsigned int getDefaultOutputDevice( void ) override;
unsigned int getDefaultInputDevice( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
// will most likely produce highly undesirable results!
bool callbackEvent( long bufferIndex );
private:
@@ -988,7 +977,7 @@ public:
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
RtAudio::StreamOptions *options ) override;
};
#endif
@@ -1001,20 +990,20 @@ public:
RtApiDs();
~RtApiDs();
RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; }
unsigned int getDeviceCount( void );
unsigned int getDefaultOutputDevice( void );
unsigned int getDefaultInputDevice( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_DS; }
unsigned int getDeviceCount( void ) override;
unsigned int getDefaultOutputDevice( void ) override;
unsigned int getDefaultInputDevice( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
// will most likely produce highly undesirable results!
void callbackEvent( void );
private:
@@ -1026,7 +1015,7 @@ public:
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
RtAudio::StreamOptions *options ) override;
};
#endif
@@ -1041,15 +1030,13 @@ public:
RtApiWasapi();
virtual ~RtApiWasapi();
RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; }
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
unsigned int getDefaultOutputDevice( void );
unsigned int getDefaultInputDevice( void );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_WASAPI; }
unsigned int getDeviceCount( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
private:
bool coInitialized_;
@@ -1058,7 +1045,7 @@ private:
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int* bufferSize,
RtAudio::StreamOptions* options );
RtAudio::StreamOptions* options ) override;
static DWORD WINAPI runWasapiThread( void* wasapiPtr );
static DWORD WINAPI stopWasapiThread( void* wasapiPtr );
@@ -1076,18 +1063,18 @@ public:
RtApiAlsa();
~RtApiAlsa();
RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; }
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_ALSA; }
unsigned int getDeviceCount( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
// will most likely produce highly undesirable results!
void callbackEvent( void );
private:
@@ -1097,7 +1084,7 @@ public:
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
RtAudio::StreamOptions *options ) override;
};
#endif
@@ -1108,28 +1095,27 @@ class RtApiPulse: public RtApi
{
public:
~RtApiPulse();
RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; }
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_PULSE; }
unsigned int getDeviceCount( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
// will most likely produce highly undesirable results!
void callbackEvent( void );
private:
std::vector<RtAudio::DeviceInfo> devices_;
void saveDeviceInfo( void );
void collectDeviceInfo( void );
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
RtAudio::StreamOptions *options ) override;
};
#endif
@@ -1142,18 +1128,18 @@ public:
RtApiOss();
~RtApiOss();
RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; }
unsigned int getDeviceCount( void );
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
void closeStream( void );
void startStream( void );
void stopStream( void );
void abortStream( void );
RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_OSS; }
unsigned int getDeviceCount( void ) override;
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
void closeStream( void ) override;
void startStream( void ) override;
void stopStream( void ) override;
void abortStream( void ) override;
// This function is intended for internal use only. It must be
// public because it is called by the internal callback handler,
// which is not a member of RtAudio. External use of this function
// will most likely produce highly undesireable results!
// will most likely produce highly undesirable results!
void callbackEvent( void );
private:
@@ -1161,7 +1147,7 @@ public:
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options );
RtAudio::StreamOptions *options ) override;
};
#endif
@@ -1173,20 +1159,20 @@ class RtApiDummy: public RtApi
public:
RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); }
RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; }
unsigned int getDeviceCount( void ) { return 0; }
RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; }
void closeStream( void ) {}
void startStream( void ) {}
void stopStream( void ) {}
void abortStream( void ) {}
RtAudio::Api getCurrentApi( void ) override { return RtAudio::RTAUDIO_DUMMY; }
unsigned int getDeviceCount( void ) override { return 0; }
RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) override { RtAudio::DeviceInfo info; return info; }
void closeStream( void ) override {}
void startStream( void ) override {}
void stopStream( void ) override {}
void abortStream( void ) override {}
private:
bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,
unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,
RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,
RtAudio::StreamOptions * /*options*/ ) { return false; }
RtAudio::StreamOptions * /*options*/ ) override { return false; }
};
#endif

View File

@@ -58,13 +58,14 @@
#endif
#endif
#define RTMIDI_VERSION "4.0.0"
#define RTMIDI_VERSION "5.0.0"
#include <exception>
#include <iostream>
#include <string>
#include <vector>
/************************************************************************/
/*! \class RtMidiError
\brief Exception handling class for RtMidi.
@@ -132,6 +133,8 @@ class MidiApi;
class RTMIDI_DLL_PUBLIC RtMidi
{
public:
RtMidi(RtMidi&& other) noexcept;
//! MIDI API specifier arguments.
enum Api {
UNSPECIFIED, /*!< Search for a working compiled API. */
@@ -140,6 +143,7 @@ class RTMIDI_DLL_PUBLIC RtMidi
UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */
WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */
RTMIDI_DUMMY, /*!< A compilable but non-functional API. */
WEB_MIDI_API, /*!< W3C Web MIDI API. */
NUM_APIS /*!< Number of values in this enum. */
};
@@ -213,6 +217,10 @@ class RTMIDI_DLL_PUBLIC RtMidi
RtMidi();
virtual ~RtMidi();
MidiApi *rtapi_;
/* Make the class non-copyable */
RtMidi(RtMidi& other) = delete;
RtMidi& operator=(RtMidi& other) = delete;
};
/**********************************************************************/
@@ -248,7 +256,6 @@ class RTMIDI_DLL_PUBLIC RtMidi
class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi
{
public:
//! User callback function type definition.
typedef void (*RtMidiCallback)( double timeStamp, std::vector<unsigned char> *message, void *userData );
@@ -274,6 +281,8 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi
const std::string& clientName = "RtMidi Input Client",
unsigned int queueSizeLimit = 100 );
RtMidiIn(RtMidiIn&& other) noexcept : RtMidi(std::move(other)) { }
//! If a MIDI connection is still open, it will be closed by the destructor.
~RtMidiIn ( void ) throw();
@@ -371,6 +380,19 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi
*/
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );
//! Set maximum expected incoming message size.
/*!
For APIs that require manual buffer management, it can be useful to set the buffer
size and buffer count when expecting to receive large SysEx messages. Note that
currently this function has no effect when called after openPort(). The default
buffer size is 1024 with a count of 4 buffers, which should be sufficient for most
cases; as mentioned, this does not affect all API backends, since most either support
dynamically scalable buffers or take care of buffer handling themselves. It is
principally intended for users of the Windows MM backend who must support receiving
especially large messages.
*/
virtual void setBufferSize( unsigned int size, unsigned int count );
protected:
void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit );
};
@@ -403,6 +425,8 @@ class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi
RtMidiOut( RtMidi::Api api=UNSPECIFIED,
const std::string& clientName = "RtMidi Output Client" );
RtMidiOut(RtMidiOut&& other) noexcept : RtMidi(std::move(other)) { }
//! The destructor closes any open MIDI connections.
~RtMidiOut( void ) throw();
@@ -523,6 +547,7 @@ protected:
RtMidiErrorCallback errorCallback_;
bool firstErrorOccurred_;
void *errorCallbackUserData_;
};
class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
@@ -535,6 +560,7 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
void cancelCallback( void );
virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense );
double getMessage( std::vector<unsigned char> *message );
virtual void setBufferSize( unsigned int size, unsigned int count );
// A MIDI structure used internally by the class to store incoming
// messages. Each message represents one and only one MIDI message.
@@ -576,11 +602,13 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
RtMidiIn::RtMidiCallback userCallback;
void *userData;
bool continueSysex;
unsigned int bufferSize;
unsigned int bufferCount;
// Default constructor.
RtMidiInData()
: ignoreFlags(7), doInput(false), firstMessage(true), apiData(0), usingCallback(false),
userCallback(0), userData(0), continueSysex(false) {}
userCallback(0), userData(0), continueSysex(false), bufferSize(1024), bufferCount(4) {}
};
protected:
@@ -614,6 +642,7 @@ inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return r
inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { static_cast<MidiInApi *>(rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); }
inline double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) { return static_cast<MidiInApi *>(rtapi_)->getMessage( message ); }
inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); }
inline void RtMidiIn :: setBufferSize( unsigned int size, unsigned int count ) { static_cast<MidiInApi *>(rtapi_)->setBufferSize(size, count); }
inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }

File diff suppressed because it is too large Load Diff

View File

@@ -40,11 +40,31 @@
#include "RtMidi.h"
#include <sstream>
#if defined(__MACOSX_CORE__)
#if TARGET_OS_IPHONE
#if (TARGET_OS_IPHONE == 1)
#define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
#define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
#endif
#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
// Default for Windows is to add an identifier to the port names; this
@@ -57,11 +77,12 @@
//
// **************************************************************** //
#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__)
#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
{
@@ -78,6 +99,7 @@ class MidiInCore: public MidiInApi
std::string getPortName( unsigned int portNumber );
protected:
MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
void initialize( const std::string& clientName );
};
@@ -97,6 +119,7 @@ class MidiOutCore: public MidiOutApi
void sendMessage( const unsigned char *message, size_t size );
protected:
MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
void initialize( const std::string& clientName );
};
@@ -231,6 +254,57 @@ class MidiOutWinMM: public MidiOutApi
#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
@@ -285,6 +359,11 @@ RtMidi :: ~RtMidi()
rtapi_ = 0;
}
RtMidi::RtMidi(RtMidi&& other) noexcept {
rtapi_ = other.rtapi_;
other.rtapi_ = nullptr;
}
std::string RtMidi :: getVersion( void ) throw()
{
return std::string( RTMIDI_VERSION );
@@ -299,6 +378,7 @@ const char* rtmidi_api_names[][2] = {
{ "alsa" , "ALSA" },
{ "jack" , "Jack" },
{ "winmm" , "Windows MultiMedia" },
{ "web" , "Web MIDI API" },
{ "dummy" , "Dummy" },
};
const unsigned int rtmidi_num_api_names =
@@ -319,6 +399,9 @@ extern "C" const RtMidi::Api rtmidi_compiled_apis[] = {
#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
@@ -344,14 +427,14 @@ void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()
std::string RtMidi :: getApiName( RtMidi::Api api )
{
if (api < 0 || api >= RtMidi::NUM_APIS)
if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS)
return "";
return rtmidi_api_names[api][0];
}
std::string RtMidi :: getApiDisplayName( RtMidi::Api api )
{
if (api < 0 || api >= RtMidi::NUM_APIS)
if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS)
return "Unknown";
return rtmidi_api_names[api][1];
}
@@ -401,6 +484,10 @@ void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, un
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 );
@@ -469,6 +556,10 @@ void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName )
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 );
@@ -634,6 +725,12 @@ double MidiInApi :: getMessage( std::vector<unsigned char> *message )
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 )
{
@@ -716,10 +813,11 @@ MidiOutApi :: ~MidiOutApi( void )
// MIDI input. We convert the system specific time stamps to delta
// time values.
// OS-X CoreMIDI header files.
#include <CoreMIDI/CoreMIDI.h>
#include <CoreAudio/HostTime.h>
#include <CoreServices/CoreServices.h>
// These are not available on iOS.
#if (TARGET_OS_IPHONE == 0)
#include <CoreAudio/HostTime.h>
#include <CoreServices/CoreServices.h>
#endif
// A structure to hold variables related to the CoreMIDI API
// implementation.
@@ -732,6 +830,20 @@ struct CoreMidiData {
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
@@ -899,15 +1011,16 @@ MidiInCore :: ~MidiInCore( void )
// Cleanup.
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
MIDIClientDispose( data->client );
if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
delete data;
}
void MidiInCore :: initialize( const std::string& clientName )
{
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 ) {
@@ -915,8 +1028,20 @@ void MidiInCore :: initialize( const std::string& clientName )
ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
errorString_ = ost.str();
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
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;
@@ -924,7 +1049,6 @@ void MidiInCore :: initialize( const std::string& clientName )
data->endpoint = 0;
apiData_ = (void *) data;
inputData_.apiData = (void *) data;
CFRelease( name );
}
void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName )
@@ -960,7 +1084,6 @@ void MidiInCore :: openPort( unsigned int portNumber, const std::string &portNam
CFRelease( portNameRef );
if ( result != noErr ) {
MIDIClientDispose( data->client );
errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
@@ -970,7 +1093,6 @@ void MidiInCore :: openPort( unsigned int portNumber, const std::string &portNam
MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
if ( endpoint == 0 ) {
MIDIPortDispose( port );
MIDIClientDispose( data->client );
errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
@@ -980,7 +1102,6 @@ void MidiInCore :: openPort( unsigned int portNumber, const std::string &portNam
result = MIDIPortConnectSource( port, endpoint, NULL );
if ( result != noErr ) {
MIDIPortDispose( port );
MIDIClientDispose( data->client );
errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
@@ -1068,6 +1189,11 @@ CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
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 )
@@ -1223,15 +1349,16 @@ MidiOutCore :: ~MidiOutCore( void )
// Cleanup.
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
MIDIClientDispose( data->client );
if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
delete data;
}
void MidiOutCore :: initialize( const std::string& clientName )
{
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 ) {
@@ -1239,15 +1366,26 @@ void MidiOutCore :: initialize( const std::string& clientName )
ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
errorString_ = ost.str();
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
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;
CFRelease( name );
}
unsigned int MidiOutCore :: getPortCount()
@@ -1310,7 +1448,6 @@ void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portNa
OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port );
CFRelease( portNameRef );
if ( result != noErr ) {
MIDIClientDispose( data->client );
errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
@@ -1320,7 +1457,6 @@ void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portNa
MIDIEndpointRef destination = MIDIGetDestination( portNumber );
if ( destination == 0 ) {
MIDIPortDispose( port );
MIDIClientDispose( data->client );
errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
error( RtMidiError::DRIVER_ERROR, errorString_ );
return;
@@ -1402,28 +1538,31 @@ void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )
return;
}
MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
OSStatus result;
if ( message[0] != 0xF0 && nBytes > 3 ) {
errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
error( RtMidiError::WARNING, errorString_ );
return;
}
Byte buffer[nBytes+(sizeof( MIDIPacketList ))];
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;
MIDIPacket *packet = MIDIPacketListInit( packetList );
ByteCount remainingBytes = nBytes;
while ( remainingBytes && packet ) {
ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket
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";
@@ -1448,6 +1587,7 @@ void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )
error( RtMidiError::WARNING, errorString_ );
}
}
}
}
#endif // __MACOSX_CORE__
@@ -1487,6 +1627,7 @@ struct AlsaMidiData {
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;
@@ -1517,7 +1658,6 @@ static void *alsaMidiHandler( void *ptr )
snd_seq_event_t *ev;
int result;
apiData->bufferSize = 32;
result = snd_midi_event_new( 0, &apiData->coder );
if ( result < 0 ) {
data->doInput = false;
@@ -1769,6 +1909,7 @@ void MidiInAlsa :: initialize( const std::string& clientName )
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;
@@ -2293,7 +2434,7 @@ void MidiOutAlsa :: openVirtualPort( const std::string &portName )
void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
{
int result;
long result;
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
unsigned int nBytes = static_cast<unsigned int> (size);
if ( nBytes > data->bufferSize ) {
@@ -2313,19 +2454,31 @@ void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
}
}
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 );
for ( unsigned int i=0; i<nBytes; ++i ) data->buffer[i] = message[i];
result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
if ( result < (int)nBytes ) {
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 ) {
@@ -2333,6 +2486,7 @@ void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
error( RtMidiError::WARNING, errorString_ );
return;
}
}
snd_seq_drain_output( data->seq );
}
@@ -2386,9 +2540,6 @@ static std::string ConvertToUTF8(const TCHAR *str)
return u8str;
}
#define RT_SYSEX_BUFFER_SIZE 1024
#define RT_SYSEX_BUFFER_COUNT 4
// A structure to hold variables related to the CoreMIDI API
// implementation.
struct WinMidiData {
@@ -2396,7 +2547,7 @@ struct WinMidiData {
HMIDIOUT outHandle; // Handle to Midi Output Device
DWORD lastTime;
MidiInApi::MidiMessage message;
LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];
std::vector<LPMIDIHDR> sysexBuffer;
CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
};
@@ -2576,10 +2727,11 @@ void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*port
}
// Allocate and init the sysex buffers.
for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
data->sysexBuffer.resize( inputData_.bufferCount );
for ( int i=0; i < inputData_.bufferCount; ++i ) {
data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
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;
@@ -2630,7 +2782,7 @@ void MidiInWinMM :: closePort( void )
midiInReset( data->inHandle );
midiInStop( data->inHandle );
for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
for ( int i=0; i < data->sysexBuffer.size(); ++i ) {
int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
delete [] data->sysexBuffer[i]->lpData;
delete [] data->sysexBuffer[i];
@@ -2811,7 +2963,10 @@ void MidiOutWinMM :: closePort( void )
{
if ( connected_ ) {
WinMidiData *data = static_cast<WinMidiData *> (apiData_);
midiOutReset( data->outHandle );
// 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;
@@ -2936,6 +3091,7 @@ void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size )
#include <jack/jack.h>
#include <jack/midiport.h>
#include <jack/ringbuffer.h>
#include <pthread.h>
#ifdef HAVE_SEMAPHORE
#include <semaphore.h>
#endif
@@ -2945,8 +3101,8 @@ void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size )
struct JackMidiData {
jack_client_t *client;
jack_port_t *port;
jack_ringbuffer_t *buffSize;
jack_ringbuffer_t *buffMessage;
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;
@@ -3101,6 +3257,8 @@ void MidiInJack :: openPort( unsigned int portNumber, const std::string &portNam
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;
}
@@ -3123,6 +3281,8 @@ void MidiInJack :: openVirtualPort( const std::string &portName )
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_ );
}
}
@@ -3226,11 +3386,15 @@ static int jackProcessOut( jack_nframes_t nframes, void *arg )
void *buff = jack_port_get_buffer( data->port, nframes );
jack_midi_clear_buffer( buff );
while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof( space ) );
midiData = jack_midi_event_reserve( buff, 0, space );
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) );
jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) 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
@@ -3269,8 +3433,8 @@ void MidiOutJack :: connect()
return;
// Initialize output ringbuffers
data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
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 ) {
@@ -3289,8 +3453,7 @@ MidiOutJack :: ~MidiOutJack()
MidiOutJack::closePort();
// Cleanup
jack_ringbuffer_free( data->buffSize );
jack_ringbuffer_free( data->buffMessage );
jack_ringbuffer_free( data->buff );
if ( data->client ) {
jack_client_close( data->client );
}
@@ -3316,6 +3479,8 @@ void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portNa
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;
}
@@ -3338,6 +3503,8 @@ void MidiOutJack :: openVirtualPort( const std::string &portName )
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_ );
}
}
@@ -3437,9 +3604,331 @@ 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->buffMessage, ( const char * ) message, nBytes );
jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
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__