mirror of
https://github.com/thestk/stk
synced 2026-01-11 20:11:52 +00:00
New versions of RtAudio and RtMidi in preparation for new release.
This commit is contained in:
@@ -46,7 +46,7 @@
|
|||||||
#ifndef __RTAUDIO_H
|
#ifndef __RTAUDIO_H
|
||||||
#define __RTAUDIO_H
|
#define __RTAUDIO_H
|
||||||
|
|
||||||
#define RTAUDIO_VERSION "5.1.0"
|
#define RTAUDIO_VERSION "5.2.0"
|
||||||
|
|
||||||
#if defined _WIN32 || defined __CYGWIN__
|
#if defined _WIN32 || defined __CYGWIN__
|
||||||
#if defined(RTAUDIO_EXPORT)
|
#if defined(RTAUDIO_EXPORT)
|
||||||
@@ -226,12 +226,12 @@ class RTAUDIO_DLL_PUBLIC RtAudioError : public std::runtime_error
|
|||||||
UNSPECIFIED, /*!< The default, unspecified error type. */
|
UNSPECIFIED, /*!< The default, unspecified error type. */
|
||||||
NO_DEVICES_FOUND, /*!< No devices found on system. */
|
NO_DEVICES_FOUND, /*!< No devices found on system. */
|
||||||
INVALID_DEVICE, /*!< An invalid device ID was specified. */
|
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_PARAMETER, /*!< An invalid parameter was specified to a function. */
|
||||||
INVALID_USE, /*!< The function was called incorrectly. */
|
INVALID_USE, /*!< The function was called incorrectly. */
|
||||||
DRIVER_ERROR, /*!< A system driver error occured. */
|
DRIVER_ERROR, /*!< A system driver error occurred. */
|
||||||
SYSTEM_ERROR, /*!< A system error occured. */
|
SYSTEM_ERROR, /*!< A system error occurred. */
|
||||||
THREAD_ERROR /*!< A thread error occured. */
|
THREAD_ERROR /*!< A thread error occurred. */
|
||||||
};
|
};
|
||||||
|
|
||||||
//! The constructor.
|
//! The constructor.
|
||||||
@@ -299,30 +299,21 @@ class RTAUDIO_DLL_PUBLIC RtAudio
|
|||||||
struct DeviceInfo {
|
struct DeviceInfo {
|
||||||
bool probed; /*!< true if the device capabilities were successfully probed. */
|
bool probed; /*!< true if the device capabilities were successfully probed. */
|
||||||
std::string name; /*!< Character string device identifier. */
|
std::string name; /*!< Character string device identifier. */
|
||||||
unsigned int outputChannels; /*!< Maximum output channels supported by device. */
|
unsigned int outputChannels{}; /*!< Maximum output channels supported by device. */
|
||||||
unsigned int inputChannels; /*!< Maximum input channels supported by device. */
|
unsigned int inputChannels{}; /*!< Maximum input channels supported by device. */
|
||||||
unsigned int duplexChannels; /*!< Maximum simultaneous input/output 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 isDefaultOutput{false}; /*!< true if this is the default output device. */
|
||||||
bool isDefaultInput; /*!< true if this is the default input 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). */
|
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. */
|
unsigned int preferredSampleRate{}; /*!< Preferred sample rate, e.g. for WASAPI the system sample rate. */
|
||||||
RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */
|
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) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! The structure for specifying input or ouput stream parameters.
|
//! The structure for specifying input or output stream parameters.
|
||||||
struct StreamParameters {
|
struct StreamParameters {
|
||||||
unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */
|
unsigned int deviceId{}; /*!< Device index (0 to getDeviceCount() - 1). */
|
||||||
unsigned int nChannels; /*!< Number of channels. */
|
unsigned int nChannels{}; /*!< Number of channels. */
|
||||||
unsigned int firstChannel; /*!< First channel index on device (default = 0). */
|
unsigned int firstChannel{}; /*!< First channel index on device (default = 0). */
|
||||||
|
|
||||||
// Default constructor.
|
|
||||||
StreamParameters()
|
|
||||||
: deviceId(0), nChannels(0), firstChannel(0) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! The structure for specifying stream options.
|
//! 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.
|
RtAudio with Jack, each instance must have a unique client name.
|
||||||
*/
|
*/
|
||||||
struct StreamOptions {
|
struct StreamOptions {
|
||||||
RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */
|
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. */
|
unsigned int numberOfBuffers{}; /*!< Number of stream buffers. */
|
||||||
std::string streamName; /*!< A stream name (currently used only in Jack). */
|
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). */
|
int priority{}; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */
|
||||||
|
|
||||||
// Default constructor.
|
|
||||||
StreamOptions()
|
|
||||||
: flags(0), numberOfBuffers(0), priority(0) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! A static function to determine the current RtAudio version.
|
//! 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
|
lowest allowable value is used. The actual value used is
|
||||||
returned via the structure argument. The parameter is API dependent.
|
returned via the structure argument. The parameter is API dependent.
|
||||||
\param errorCallback A client-defined function that will be invoked
|
\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,
|
void openStream( RtAudio::StreamParameters *outputParameters,
|
||||||
RtAudio::StreamParameters *inputParameters,
|
RtAudio::StreamParameters *inputParameters,
|
||||||
@@ -616,7 +603,7 @@ class RTAUDIO_DLL_PUBLIC RtAudio
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Operating system dependent thread functionality.
|
// Operating system dependent thread functionality.
|
||||||
#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
@@ -628,18 +615,22 @@ class RTAUDIO_DLL_PUBLIC RtAudio
|
|||||||
typedef uintptr_t ThreadHandle;
|
typedef uintptr_t ThreadHandle;
|
||||||
typedef CRITICAL_SECTION StreamMutex;
|
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.
|
// Using pthread library for various flavors of unix.
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
typedef pthread_t ThreadHandle;
|
typedef pthread_t ThreadHandle;
|
||||||
typedef pthread_mutex_t StreamMutex;
|
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__
|
#define __RTAUDIO_DUMMY__
|
||||||
typedef int ThreadHandle;
|
|
||||||
typedef int StreamMutex;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -647,19 +638,15 @@ class RTAUDIO_DLL_PUBLIC RtAudio
|
|||||||
// between the private RtAudio stream structure and global callback
|
// between the private RtAudio stream structure and global callback
|
||||||
// handling functions.
|
// handling functions.
|
||||||
struct CallbackInfo {
|
struct CallbackInfo {
|
||||||
void *object; // Used as a "this" pointer.
|
void *object{}; // Used as a "this" pointer.
|
||||||
ThreadHandle thread;
|
ThreadHandle thread{};
|
||||||
void *callback;
|
void *callback{};
|
||||||
void *userData;
|
void *userData{};
|
||||||
void *errorCallback;
|
void *errorCallback{};
|
||||||
void *apiInfo; // void pointer for API specific callback information
|
void *apiInfo{}; // void pointer for API specific callback information
|
||||||
bool isRunning;
|
bool isRunning{false};
|
||||||
bool doRealtime;
|
bool doRealtime{false};
|
||||||
int priority;
|
int priority{};
|
||||||
|
|
||||||
// Default constructor.
|
|
||||||
CallbackInfo()
|
|
||||||
:object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false), priority(0) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// **************************************************************** //
|
// **************************************************************** //
|
||||||
@@ -686,9 +673,9 @@ class S24 {
|
|||||||
S24() {}
|
S24() {}
|
||||||
|
|
||||||
S24& operator = ( const int& i ) {
|
S24& operator = ( const int& i ) {
|
||||||
c3[0] = (i & 0x000000ff);
|
c3[0] = (unsigned char)(i & 0x000000ff);
|
||||||
c3[1] = (i & 0x0000ff00) >> 8;
|
c3[1] = (unsigned char)((i & 0x0000ff00) >> 8);
|
||||||
c3[2] = (i & 0x00ff0000) >> 16;
|
c3[2] = (unsigned char)((i & 0x00ff0000) >> 16);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -895,20 +882,20 @@ public:
|
|||||||
|
|
||||||
RtApiCore();
|
RtApiCore();
|
||||||
~RtApiCore();
|
~RtApiCore();
|
||||||
RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; }
|
RtAudio::Api getCurrentApi( void ) override { return RtAudio::MACOSX_CORE; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
unsigned int getDefaultOutputDevice( void );
|
unsigned int getDefaultOutputDevice( void ) override;
|
||||||
unsigned int getDefaultInputDevice( void );
|
unsigned int getDefaultInputDevice( void ) override;
|
||||||
void closeStream( void );
|
void closeStream( void ) override;
|
||||||
void startStream( void );
|
void startStream( void ) override;
|
||||||
void stopStream( void );
|
void stopStream( void ) override;
|
||||||
void abortStream( void );
|
void abortStream( void ) override;
|
||||||
|
|
||||||
// This function is intended for internal use only. It must be
|
// This function is intended for internal use only. It must be
|
||||||
// public because it is called by the internal callback handler,
|
// public because it is called by the internal callback handler,
|
||||||
// which is not a member of RtAudio. External use of this function
|
// 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,
|
bool callbackEvent( AudioDeviceID deviceId,
|
||||||
const AudioBufferList *inBufferList,
|
const AudioBufferList *inBufferList,
|
||||||
const AudioBufferList *outBufferList );
|
const AudioBufferList *outBufferList );
|
||||||
@@ -918,7 +905,7 @@ public:
|
|||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int *bufferSize,
|
RtAudioFormat format, unsigned int *bufferSize,
|
||||||
RtAudio::StreamOptions *options );
|
RtAudio::StreamOptions *options ) override;
|
||||||
static const char* getErrorCode( OSStatus code );
|
static const char* getErrorCode( OSStatus code );
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -932,18 +919,18 @@ public:
|
|||||||
|
|
||||||
RtApiJack();
|
RtApiJack();
|
||||||
~RtApiJack();
|
~RtApiJack();
|
||||||
RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; }
|
RtAudio::Api getCurrentApi( void ) override { return RtAudio::UNIX_JACK; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
void closeStream( void );
|
void closeStream( void ) override;
|
||||||
void startStream( void );
|
void startStream( void ) override;
|
||||||
void stopStream( void );
|
void stopStream( void ) override;
|
||||||
void abortStream( void );
|
void abortStream( void ) override;
|
||||||
|
|
||||||
// This function is intended for internal use only. It must be
|
// This function is intended for internal use only. It must be
|
||||||
// public because it is called by the internal callback handler,
|
// public because it is called by the internal callback handler,
|
||||||
// which is not a member of RtAudio. External use of this function
|
// 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 );
|
bool callbackEvent( unsigned long nframes );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -951,7 +938,7 @@ public:
|
|||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int *bufferSize,
|
RtAudioFormat format, unsigned int *bufferSize,
|
||||||
RtAudio::StreamOptions *options );
|
RtAudio::StreamOptions *options ) override;
|
||||||
|
|
||||||
bool shouldAutoconnect_;
|
bool shouldAutoconnect_;
|
||||||
};
|
};
|
||||||
@@ -966,18 +953,20 @@ public:
|
|||||||
|
|
||||||
RtApiAsio();
|
RtApiAsio();
|
||||||
~RtApiAsio();
|
~RtApiAsio();
|
||||||
RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; }
|
RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_ASIO; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
unsigned int getDefaultOutputDevice( void ) override;
|
||||||
void closeStream( void );
|
unsigned int getDefaultInputDevice( void ) override;
|
||||||
void startStream( void );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
void stopStream( void );
|
void closeStream( void ) override;
|
||||||
void abortStream( void );
|
void startStream( void ) override;
|
||||||
|
void stopStream( void ) override;
|
||||||
|
void abortStream( void ) override;
|
||||||
|
|
||||||
// This function is intended for internal use only. It must be
|
// This function is intended for internal use only. It must be
|
||||||
// public because it is called by the internal callback handler,
|
// public because it is called by the internal callback handler,
|
||||||
// which is not a member of RtAudio. External use of this function
|
// 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 );
|
bool callbackEvent( long bufferIndex );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -988,7 +977,7 @@ public:
|
|||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int *bufferSize,
|
RtAudioFormat format, unsigned int *bufferSize,
|
||||||
RtAudio::StreamOptions *options );
|
RtAudio::StreamOptions *options ) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1001,20 +990,20 @@ public:
|
|||||||
|
|
||||||
RtApiDs();
|
RtApiDs();
|
||||||
~RtApiDs();
|
~RtApiDs();
|
||||||
RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; }
|
RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_DS; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
unsigned int getDefaultOutputDevice( void );
|
unsigned int getDefaultOutputDevice( void ) override;
|
||||||
unsigned int getDefaultInputDevice( void );
|
unsigned int getDefaultInputDevice( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
void closeStream( void );
|
void closeStream( void ) override;
|
||||||
void startStream( void );
|
void startStream( void ) override;
|
||||||
void stopStream( void );
|
void stopStream( void ) override;
|
||||||
void abortStream( void );
|
void abortStream( void ) override;
|
||||||
|
|
||||||
// This function is intended for internal use only. It must be
|
// This function is intended for internal use only. It must be
|
||||||
// public because it is called by the internal callback handler,
|
// public because it is called by the internal callback handler,
|
||||||
// which is not a member of RtAudio. External use of this function
|
// 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 );
|
void callbackEvent( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1026,7 +1015,7 @@ public:
|
|||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int *bufferSize,
|
RtAudioFormat format, unsigned int *bufferSize,
|
||||||
RtAudio::StreamOptions *options );
|
RtAudio::StreamOptions *options ) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1041,15 +1030,13 @@ public:
|
|||||||
RtApiWasapi();
|
RtApiWasapi();
|
||||||
virtual ~RtApiWasapi();
|
virtual ~RtApiWasapi();
|
||||||
|
|
||||||
RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; }
|
RtAudio::Api getCurrentApi( void ) override { return RtAudio::WINDOWS_WASAPI; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
unsigned int getDefaultOutputDevice( void );
|
void closeStream( void ) override;
|
||||||
unsigned int getDefaultInputDevice( void );
|
void startStream( void ) override;
|
||||||
void closeStream( void );
|
void stopStream( void ) override;
|
||||||
void startStream( void );
|
void abortStream( void ) override;
|
||||||
void stopStream( void );
|
|
||||||
void abortStream( void );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool coInitialized_;
|
bool coInitialized_;
|
||||||
@@ -1058,7 +1045,7 @@ private:
|
|||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int* bufferSize,
|
RtAudioFormat format, unsigned int* bufferSize,
|
||||||
RtAudio::StreamOptions* options );
|
RtAudio::StreamOptions* options ) override;
|
||||||
|
|
||||||
static DWORD WINAPI runWasapiThread( void* wasapiPtr );
|
static DWORD WINAPI runWasapiThread( void* wasapiPtr );
|
||||||
static DWORD WINAPI stopWasapiThread( void* wasapiPtr );
|
static DWORD WINAPI stopWasapiThread( void* wasapiPtr );
|
||||||
@@ -1076,18 +1063,18 @@ public:
|
|||||||
|
|
||||||
RtApiAlsa();
|
RtApiAlsa();
|
||||||
~RtApiAlsa();
|
~RtApiAlsa();
|
||||||
RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; }
|
RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_ALSA; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
void closeStream( void );
|
void closeStream( void ) override;
|
||||||
void startStream( void );
|
void startStream( void ) override;
|
||||||
void stopStream( void );
|
void stopStream( void ) override;
|
||||||
void abortStream( void );
|
void abortStream( void ) override;
|
||||||
|
|
||||||
// This function is intended for internal use only. It must be
|
// This function is intended for internal use only. It must be
|
||||||
// public because it is called by the internal callback handler,
|
// public because it is called by the internal callback handler,
|
||||||
// which is not a member of RtAudio. External use of this function
|
// 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 );
|
void callbackEvent( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1097,7 +1084,7 @@ public:
|
|||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int *bufferSize,
|
RtAudioFormat format, unsigned int *bufferSize,
|
||||||
RtAudio::StreamOptions *options );
|
RtAudio::StreamOptions *options ) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1108,28 +1095,27 @@ class RtApiPulse: public RtApi
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~RtApiPulse();
|
~RtApiPulse();
|
||||||
RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; }
|
RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_PULSE; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
void closeStream( void );
|
void closeStream( void ) override;
|
||||||
void startStream( void );
|
void startStream( void ) override;
|
||||||
void stopStream( void );
|
void stopStream( void ) override;
|
||||||
void abortStream( void );
|
void abortStream( void ) override;
|
||||||
|
|
||||||
// This function is intended for internal use only. It must be
|
// This function is intended for internal use only. It must be
|
||||||
// public because it is called by the internal callback handler,
|
// public because it is called by the internal callback handler,
|
||||||
// which is not a member of RtAudio. External use of this function
|
// 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 );
|
void callbackEvent( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<RtAudio::DeviceInfo> devices_;
|
void collectDeviceInfo( void );
|
||||||
void saveDeviceInfo( void );
|
|
||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int *bufferSize,
|
RtAudioFormat format, unsigned int *bufferSize,
|
||||||
RtAudio::StreamOptions *options );
|
RtAudio::StreamOptions *options ) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1142,18 +1128,18 @@ public:
|
|||||||
|
|
||||||
RtApiOss();
|
RtApiOss();
|
||||||
~RtApiOss();
|
~RtApiOss();
|
||||||
RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; }
|
RtAudio::Api getCurrentApi() override { return RtAudio::LINUX_OSS; }
|
||||||
unsigned int getDeviceCount( void );
|
unsigned int getDeviceCount( void ) override;
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) override;
|
||||||
void closeStream( void );
|
void closeStream( void ) override;
|
||||||
void startStream( void );
|
void startStream( void ) override;
|
||||||
void stopStream( void );
|
void stopStream( void ) override;
|
||||||
void abortStream( void );
|
void abortStream( void ) override;
|
||||||
|
|
||||||
// This function is intended for internal use only. It must be
|
// This function is intended for internal use only. It must be
|
||||||
// public because it is called by the internal callback handler,
|
// public because it is called by the internal callback handler,
|
||||||
// which is not a member of RtAudio. External use of this function
|
// 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 );
|
void callbackEvent( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1161,7 +1147,7 @@ public:
|
|||||||
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
|
||||||
unsigned int firstChannel, unsigned int sampleRate,
|
unsigned int firstChannel, unsigned int sampleRate,
|
||||||
RtAudioFormat format, unsigned int *bufferSize,
|
RtAudioFormat format, unsigned int *bufferSize,
|
||||||
RtAudio::StreamOptions *options );
|
RtAudio::StreamOptions *options ) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1173,20 +1159,20 @@ class RtApiDummy: public RtApi
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); }
|
RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); }
|
||||||
RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; }
|
RtAudio::Api getCurrentApi( void ) override { return RtAudio::RTAUDIO_DUMMY; }
|
||||||
unsigned int getDeviceCount( void ) { return 0; }
|
unsigned int getDeviceCount( void ) override { return 0; }
|
||||||
RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; }
|
RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) override { RtAudio::DeviceInfo info; return info; }
|
||||||
void closeStream( void ) {}
|
void closeStream( void ) override {}
|
||||||
void startStream( void ) {}
|
void startStream( void ) override {}
|
||||||
void stopStream( void ) {}
|
void stopStream( void ) override {}
|
||||||
void abortStream( void ) {}
|
void abortStream( void ) override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,
|
bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,
|
||||||
unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,
|
unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,
|
||||||
RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,
|
RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,
|
||||||
RtAudio::StreamOptions * /*options*/ ) { return false; }
|
RtAudio::StreamOptions * /*options*/ ) override { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -58,13 +58,14 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define RTMIDI_VERSION "4.0.0"
|
#define RTMIDI_VERSION "5.0.0"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/*! \class RtMidiError
|
/*! \class RtMidiError
|
||||||
\brief Exception handling class for RtMidi.
|
\brief Exception handling class for RtMidi.
|
||||||
@@ -132,6 +133,8 @@ class MidiApi;
|
|||||||
class RTMIDI_DLL_PUBLIC RtMidi
|
class RTMIDI_DLL_PUBLIC RtMidi
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
RtMidi(RtMidi&& other) noexcept;
|
||||||
//! MIDI API specifier arguments.
|
//! MIDI API specifier arguments.
|
||||||
enum Api {
|
enum Api {
|
||||||
UNSPECIFIED, /*!< Search for a working compiled 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. */
|
UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */
|
||||||
WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */
|
WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */
|
||||||
RTMIDI_DUMMY, /*!< A compilable but non-functional API. */
|
RTMIDI_DUMMY, /*!< A compilable but non-functional API. */
|
||||||
|
WEB_MIDI_API, /*!< W3C Web MIDI API. */
|
||||||
NUM_APIS /*!< Number of values in this enum. */
|
NUM_APIS /*!< Number of values in this enum. */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -213,6 +217,10 @@ class RTMIDI_DLL_PUBLIC RtMidi
|
|||||||
RtMidi();
|
RtMidi();
|
||||||
virtual ~RtMidi();
|
virtual ~RtMidi();
|
||||||
MidiApi *rtapi_;
|
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
|
class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
//! User callback function type definition.
|
//! User callback function type definition.
|
||||||
typedef void (*RtMidiCallback)( double timeStamp, std::vector<unsigned char> *message, void *userData );
|
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",
|
const std::string& clientName = "RtMidi Input Client",
|
||||||
unsigned int queueSizeLimit = 100 );
|
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.
|
//! If a MIDI connection is still open, it will be closed by the destructor.
|
||||||
~RtMidiIn ( void ) throw();
|
~RtMidiIn ( void ) throw();
|
||||||
|
|
||||||
@@ -371,6 +380,19 @@ class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi
|
|||||||
*/
|
*/
|
||||||
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );
|
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:
|
protected:
|
||||||
void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit );
|
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,
|
RtMidiOut( RtMidi::Api api=UNSPECIFIED,
|
||||||
const std::string& clientName = "RtMidi Output Client" );
|
const std::string& clientName = "RtMidi Output Client" );
|
||||||
|
|
||||||
|
RtMidiOut(RtMidiOut&& other) noexcept : RtMidi(std::move(other)) { }
|
||||||
|
|
||||||
//! The destructor closes any open MIDI connections.
|
//! The destructor closes any open MIDI connections.
|
||||||
~RtMidiOut( void ) throw();
|
~RtMidiOut( void ) throw();
|
||||||
|
|
||||||
@@ -523,6 +547,7 @@ protected:
|
|||||||
RtMidiErrorCallback errorCallback_;
|
RtMidiErrorCallback errorCallback_;
|
||||||
bool firstErrorOccurred_;
|
bool firstErrorOccurred_;
|
||||||
void *errorCallbackUserData_;
|
void *errorCallbackUserData_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
|
class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
|
||||||
@@ -535,6 +560,7 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
|
|||||||
void cancelCallback( void );
|
void cancelCallback( void );
|
||||||
virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense );
|
virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense );
|
||||||
double getMessage( std::vector<unsigned char> *message );
|
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
|
// A MIDI structure used internally by the class to store incoming
|
||||||
// messages. Each message represents one and only one MIDI message.
|
// messages. Each message represents one and only one MIDI message.
|
||||||
@@ -576,11 +602,13 @@ class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
|
|||||||
RtMidiIn::RtMidiCallback userCallback;
|
RtMidiIn::RtMidiCallback userCallback;
|
||||||
void *userData;
|
void *userData;
|
||||||
bool continueSysex;
|
bool continueSysex;
|
||||||
|
unsigned int bufferSize;
|
||||||
|
unsigned int bufferCount;
|
||||||
|
|
||||||
// Default constructor.
|
// Default constructor.
|
||||||
RtMidiInData()
|
RtMidiInData()
|
||||||
: ignoreFlags(7), doInput(false), firstMessage(true), apiData(0), usingCallback(false),
|
: 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:
|
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 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 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 :: 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 RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
|
||||||
inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }
|
inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }
|
||||||
|
|||||||
751
src/RtAudio.cpp
751
src/RtAudio.cpp
File diff suppressed because it is too large
Load Diff
605
src/RtMidi.cpp
605
src/RtMidi.cpp
@@ -40,11 +40,31 @@
|
|||||||
#include "RtMidi.h"
|
#include "RtMidi.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#if defined(__MACOSX_CORE__)
|
#if (TARGET_OS_IPHONE == 1)
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
#define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
|
#define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
|
||||||
#define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
|
#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
|
#endif
|
||||||
|
|
||||||
// Default for Windows is to add an identifier to the port names; this
|
// 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__
|
#define __RTMIDI_DUMMY__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__MACOSX_CORE__)
|
#if defined(__MACOSX_CORE__)
|
||||||
|
#include <CoreMIDI/CoreMIDI.h>
|
||||||
|
|
||||||
class MidiInCore: public MidiInApi
|
class MidiInCore: public MidiInApi
|
||||||
{
|
{
|
||||||
@@ -78,6 +99,7 @@ class MidiInCore: public MidiInApi
|
|||||||
std::string getPortName( unsigned int portNumber );
|
std::string getPortName( unsigned int portNumber );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
|
||||||
void initialize( const std::string& clientName );
|
void initialize( const std::string& clientName );
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,6 +119,7 @@ class MidiOutCore: public MidiOutApi
|
|||||||
void sendMessage( const unsigned char *message, size_t size );
|
void sendMessage( const unsigned char *message, size_t size );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw();
|
||||||
void initialize( const std::string& clientName );
|
void initialize( const std::string& clientName );
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -231,6 +254,57 @@ class MidiOutWinMM: public MidiOutApi
|
|||||||
|
|
||||||
#endif
|
#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__)
|
#if defined(__RTMIDI_DUMMY__)
|
||||||
|
|
||||||
class MidiInDummy: public MidiInApi
|
class MidiInDummy: public MidiInApi
|
||||||
@@ -285,6 +359,11 @@ RtMidi :: ~RtMidi()
|
|||||||
rtapi_ = 0;
|
rtapi_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RtMidi::RtMidi(RtMidi&& other) noexcept {
|
||||||
|
rtapi_ = other.rtapi_;
|
||||||
|
other.rtapi_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string RtMidi :: getVersion( void ) throw()
|
std::string RtMidi :: getVersion( void ) throw()
|
||||||
{
|
{
|
||||||
return std::string( RTMIDI_VERSION );
|
return std::string( RTMIDI_VERSION );
|
||||||
@@ -299,6 +378,7 @@ const char* rtmidi_api_names[][2] = {
|
|||||||
{ "alsa" , "ALSA" },
|
{ "alsa" , "ALSA" },
|
||||||
{ "jack" , "Jack" },
|
{ "jack" , "Jack" },
|
||||||
{ "winmm" , "Windows MultiMedia" },
|
{ "winmm" , "Windows MultiMedia" },
|
||||||
|
{ "web" , "Web MIDI API" },
|
||||||
{ "dummy" , "Dummy" },
|
{ "dummy" , "Dummy" },
|
||||||
};
|
};
|
||||||
const unsigned int rtmidi_num_api_names =
|
const unsigned int rtmidi_num_api_names =
|
||||||
@@ -319,6 +399,9 @@ extern "C" const RtMidi::Api rtmidi_compiled_apis[] = {
|
|||||||
#if defined(__WINDOWS_MM__)
|
#if defined(__WINDOWS_MM__)
|
||||||
RtMidi::WINDOWS_MM,
|
RtMidi::WINDOWS_MM,
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(__WEB_MIDI_API__)
|
||||||
|
RtMidi::WEB_MIDI_API,
|
||||||
|
#endif
|
||||||
#if defined(__RTMIDI_DUMMY__)
|
#if defined(__RTMIDI_DUMMY__)
|
||||||
RtMidi::RTMIDI_DUMMY,
|
RtMidi::RTMIDI_DUMMY,
|
||||||
#endif
|
#endif
|
||||||
@@ -344,14 +427,14 @@ void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()
|
|||||||
|
|
||||||
std::string RtMidi :: getApiName( RtMidi::Api api )
|
std::string RtMidi :: getApiName( RtMidi::Api api )
|
||||||
{
|
{
|
||||||
if (api < 0 || api >= RtMidi::NUM_APIS)
|
if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS)
|
||||||
return "";
|
return "";
|
||||||
return rtmidi_api_names[api][0];
|
return rtmidi_api_names[api][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RtMidi :: getApiDisplayName( RtMidi::Api api )
|
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 "Unknown";
|
||||||
return rtmidi_api_names[api][1];
|
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 )
|
if ( api == MACOSX_CORE )
|
||||||
rtapi_ = new MidiInCore( clientName, queueSizeLimit );
|
rtapi_ = new MidiInCore( clientName, queueSizeLimit );
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(__WEB_MIDI_API__)
|
||||||
|
if ( api == WEB_MIDI_API )
|
||||||
|
rtapi_ = new MidiInWeb( clientName, queueSizeLimit );
|
||||||
|
#endif
|
||||||
#if defined(__RTMIDI_DUMMY__)
|
#if defined(__RTMIDI_DUMMY__)
|
||||||
if ( api == RTMIDI_DUMMY )
|
if ( api == RTMIDI_DUMMY )
|
||||||
rtapi_ = new MidiInDummy( clientName, queueSizeLimit );
|
rtapi_ = new MidiInDummy( clientName, queueSizeLimit );
|
||||||
@@ -469,6 +556,10 @@ void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName )
|
|||||||
if ( api == MACOSX_CORE )
|
if ( api == MACOSX_CORE )
|
||||||
rtapi_ = new MidiOutCore( clientName );
|
rtapi_ = new MidiOutCore( clientName );
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(__WEB_MIDI_API__)
|
||||||
|
if ( api == WEB_MIDI_API )
|
||||||
|
rtapi_ = new MidiOutWeb( clientName );
|
||||||
|
#endif
|
||||||
#if defined(__RTMIDI_DUMMY__)
|
#if defined(__RTMIDI_DUMMY__)
|
||||||
if ( api == RTMIDI_DUMMY )
|
if ( api == RTMIDI_DUMMY )
|
||||||
rtapi_ = new MidiOutDummy( clientName );
|
rtapi_ = new MidiOutDummy( clientName );
|
||||||
@@ -634,6 +725,12 @@ double MidiInApi :: getMessage( std::vector<unsigned char> *message )
|
|||||||
return timeStamp;
|
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 MidiInApi::MidiQueue::size( unsigned int *__back,
|
||||||
unsigned int *__front )
|
unsigned int *__front )
|
||||||
{
|
{
|
||||||
@@ -716,10 +813,11 @@ MidiOutApi :: ~MidiOutApi( void )
|
|||||||
// MIDI input. We convert the system specific time stamps to delta
|
// MIDI input. We convert the system specific time stamps to delta
|
||||||
// time values.
|
// time values.
|
||||||
|
|
||||||
// OS-X CoreMIDI header files.
|
// These are not available on iOS.
|
||||||
#include <CoreMIDI/CoreMIDI.h>
|
#if (TARGET_OS_IPHONE == 0)
|
||||||
#include <CoreAudio/HostTime.h>
|
#include <CoreAudio/HostTime.h>
|
||||||
#include <CoreServices/CoreServices.h>
|
#include <CoreServices/CoreServices.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// A structure to hold variables related to the CoreMIDI API
|
// A structure to hold variables related to the CoreMIDI API
|
||||||
// implementation.
|
// implementation.
|
||||||
@@ -732,6 +830,20 @@ struct CoreMidiData {
|
|||||||
MIDISysexSendRequest sysexreq;
|
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
|
// API: OS-X
|
||||||
// Class Definitions: MidiInCore
|
// Class Definitions: MidiInCore
|
||||||
@@ -899,15 +1011,16 @@ MidiInCore :: ~MidiInCore( void )
|
|||||||
|
|
||||||
// Cleanup.
|
// Cleanup.
|
||||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||||
MIDIClientDispose( data->client );
|
|
||||||
if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
|
if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
|
||||||
delete data;
|
delete data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiInCore :: initialize( const std::string& clientName )
|
MIDIClientRef MidiInCore::getCoreMidiClientSingleton(const std::string& clientName) throw() {
|
||||||
{
|
|
||||||
|
if (CoreMidiClientSingleton == 0){
|
||||||
// Set up our client.
|
// Set up our client.
|
||||||
MIDIClientRef client;
|
MIDIClientRef client;
|
||||||
|
|
||||||
CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
|
CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
|
||||||
OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
|
OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
|
||||||
if ( result != noErr ) {
|
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 << ").";
|
ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
|
||||||
errorString_ = ost.str();
|
errorString_ = ost.str();
|
||||||
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
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.
|
// Save our api-specific connection information.
|
||||||
CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
|
CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
|
||||||
@@ -924,7 +1049,6 @@ void MidiInCore :: initialize( const std::string& clientName )
|
|||||||
data->endpoint = 0;
|
data->endpoint = 0;
|
||||||
apiData_ = (void *) data;
|
apiData_ = (void *) data;
|
||||||
inputData_.apiData = (void *) data;
|
inputData_.apiData = (void *) data;
|
||||||
CFRelease( name );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName )
|
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 );
|
CFRelease( portNameRef );
|
||||||
|
|
||||||
if ( result != noErr ) {
|
if ( result != noErr ) {
|
||||||
MIDIClientDispose( data->client );
|
|
||||||
errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
|
errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
|
||||||
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
||||||
return;
|
return;
|
||||||
@@ -970,7 +1093,6 @@ void MidiInCore :: openPort( unsigned int portNumber, const std::string &portNam
|
|||||||
MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
|
MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
|
||||||
if ( endpoint == 0 ) {
|
if ( endpoint == 0 ) {
|
||||||
MIDIPortDispose( port );
|
MIDIPortDispose( port );
|
||||||
MIDIClientDispose( data->client );
|
|
||||||
errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
|
errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
|
||||||
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
||||||
return;
|
return;
|
||||||
@@ -980,7 +1102,6 @@ void MidiInCore :: openPort( unsigned int portNumber, const std::string &portNam
|
|||||||
result = MIDIPortConnectSource( port, endpoint, NULL );
|
result = MIDIPortConnectSource( port, endpoint, NULL );
|
||||||
if ( result != noErr ) {
|
if ( result != noErr ) {
|
||||||
MIDIPortDispose( port );
|
MIDIPortDispose( port );
|
||||||
MIDIClientDispose( data->client );
|
|
||||||
errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
|
errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
|
||||||
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
||||||
return;
|
return;
|
||||||
@@ -1068,6 +1189,11 @@ CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
|
|||||||
CFRelease( 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;
|
MIDIEntityRef entity = 0;
|
||||||
MIDIEndpointGetEntity( endpoint, &entity );
|
MIDIEndpointGetEntity( endpoint, &entity );
|
||||||
if ( entity == 0 )
|
if ( entity == 0 )
|
||||||
@@ -1223,15 +1349,16 @@ MidiOutCore :: ~MidiOutCore( void )
|
|||||||
|
|
||||||
// Cleanup.
|
// Cleanup.
|
||||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||||
MIDIClientDispose( data->client );
|
|
||||||
if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
|
if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
|
||||||
delete data;
|
delete data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiOutCore :: initialize( const std::string& clientName )
|
MIDIClientRef MidiOutCore::getCoreMidiClientSingleton(const std::string& clientName) throw() {
|
||||||
{
|
|
||||||
|
if (CoreMidiClientSingleton == 0){
|
||||||
// Set up our client.
|
// Set up our client.
|
||||||
MIDIClientRef client;
|
MIDIClientRef client;
|
||||||
|
|
||||||
CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
|
CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
|
||||||
OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
|
OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
|
||||||
if ( result != noErr ) {
|
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 << ").";
|
ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ").";
|
||||||
errorString_ = ost.str();
|
errorString_ = ost.str();
|
||||||
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
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.
|
// Save our api-specific connection information.
|
||||||
CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
|
CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
|
||||||
data->client = client;
|
data->client = client;
|
||||||
data->endpoint = 0;
|
data->endpoint = 0;
|
||||||
apiData_ = (void *) data;
|
apiData_ = (void *) data;
|
||||||
CFRelease( name );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int MidiOutCore :: getPortCount()
|
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 );
|
OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port );
|
||||||
CFRelease( portNameRef );
|
CFRelease( portNameRef );
|
||||||
if ( result != noErr ) {
|
if ( result != noErr ) {
|
||||||
MIDIClientDispose( data->client );
|
|
||||||
errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
|
errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
|
||||||
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
||||||
return;
|
return;
|
||||||
@@ -1320,7 +1457,6 @@ void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portNa
|
|||||||
MIDIEndpointRef destination = MIDIGetDestination( portNumber );
|
MIDIEndpointRef destination = MIDIGetDestination( portNumber );
|
||||||
if ( destination == 0 ) {
|
if ( destination == 0 ) {
|
||||||
MIDIPortDispose( port );
|
MIDIPortDispose( port );
|
||||||
MIDIClientDispose( data->client );
|
|
||||||
errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
|
errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
|
||||||
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
||||||
return;
|
return;
|
||||||
@@ -1402,28 +1538,31 @@ void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
|
|
||||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
|
||||||
OSStatus result;
|
|
||||||
|
|
||||||
if ( message[0] != 0xF0 && nBytes > 3 ) {
|
if ( message[0] != 0xF0 && nBytes > 3 ) {
|
||||||
errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
|
errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
|
||||||
error( RtMidiError::WARNING, errorString_ );
|
error( RtMidiError::WARNING, errorString_ );
|
||||||
return;
|
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 );
|
ByteCount listSize = sizeof( buffer );
|
||||||
MIDIPacketList *packetList = (MIDIPacketList*)buffer;
|
MIDIPacketList *packetList = (MIDIPacketList*)buffer;
|
||||||
MIDIPacket *packet = MIDIPacketListInit( packetList );
|
|
||||||
|
|
||||||
ByteCount remainingBytes = nBytes;
|
ByteCount remainingBytes = nBytes;
|
||||||
while ( remainingBytes && packet ) {
|
while ( remainingBytes ) {
|
||||||
ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket
|
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];
|
const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes];
|
||||||
packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr );
|
packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr );
|
||||||
remainingBytes -= bytesForPacket;
|
remainingBytes -= bytesForPacket;
|
||||||
}
|
|
||||||
|
|
||||||
if ( !packet ) {
|
if ( !packet ) {
|
||||||
errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
|
errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
|
||||||
@@ -1449,6 +1588,7 @@ void MidiOutCore :: sendMessage( const unsigned char *message, size_t size )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __MACOSX_CORE__
|
#endif // __MACOSX_CORE__
|
||||||
|
|
||||||
@@ -1487,6 +1627,7 @@ struct AlsaMidiData {
|
|||||||
snd_seq_port_subscribe_t *subscription;
|
snd_seq_port_subscribe_t *subscription;
|
||||||
snd_midi_event_t *coder;
|
snd_midi_event_t *coder;
|
||||||
unsigned int bufferSize;
|
unsigned int bufferSize;
|
||||||
|
unsigned int requestedBufferSize;
|
||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
pthread_t dummy_thread_id;
|
pthread_t dummy_thread_id;
|
||||||
@@ -1517,7 +1658,6 @@ static void *alsaMidiHandler( void *ptr )
|
|||||||
|
|
||||||
snd_seq_event_t *ev;
|
snd_seq_event_t *ev;
|
||||||
int result;
|
int result;
|
||||||
apiData->bufferSize = 32;
|
|
||||||
result = snd_midi_event_new( 0, &apiData->coder );
|
result = snd_midi_event_new( 0, &apiData->coder );
|
||||||
if ( result < 0 ) {
|
if ( result < 0 ) {
|
||||||
data->doInput = false;
|
data->doInput = false;
|
||||||
@@ -1769,6 +1909,7 @@ void MidiInAlsa :: initialize( const std::string& clientName )
|
|||||||
data->thread = data->dummy_thread_id;
|
data->thread = data->dummy_thread_id;
|
||||||
data->trigger_fds[0] = -1;
|
data->trigger_fds[0] = -1;
|
||||||
data->trigger_fds[1] = -1;
|
data->trigger_fds[1] = -1;
|
||||||
|
data->bufferSize = inputData_.bufferSize;
|
||||||
apiData_ = (void *) data;
|
apiData_ = (void *) data;
|
||||||
inputData_.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 )
|
void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
|
||||||
{
|
{
|
||||||
int result;
|
long result;
|
||||||
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
||||||
unsigned int nBytes = static_cast<unsigned int> (size);
|
unsigned int nBytes = static_cast<unsigned int> (size);
|
||||||
if ( nBytes > data->bufferSize ) {
|
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_event_t ev;
|
||||||
snd_seq_ev_clear( &ev );
|
snd_seq_ev_clear( &ev );
|
||||||
snd_seq_ev_set_source( &ev, data->vport );
|
snd_seq_ev_set_source( &ev, data->vport );
|
||||||
snd_seq_ev_set_subs( &ev );
|
snd_seq_ev_set_subs( &ev );
|
||||||
snd_seq_ev_set_direct( &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 + offset,
|
||||||
result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
|
(long)(nBytes - offset), &ev );
|
||||||
if ( result < (int)nBytes ) {
|
if ( result < 0 ) {
|
||||||
errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
|
errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
|
||||||
error( RtMidiError::WARNING, errorString_ );
|
error( RtMidiError::WARNING, errorString_ );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ev.type == SND_SEQ_EVENT_NONE ) {
|
||||||
|
errorString_ = "MidiOutAlsa::sendMessage: incomplete message!";
|
||||||
|
error( RtMidiError::WARNING, errorString_ );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += result;
|
||||||
|
|
||||||
// Send the event.
|
// Send the event.
|
||||||
result = snd_seq_event_output( data->seq, &ev );
|
result = snd_seq_event_output( data->seq, &ev );
|
||||||
if ( result < 0 ) {
|
if ( result < 0 ) {
|
||||||
@@ -2333,6 +2486,7 @@ void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size )
|
|||||||
error( RtMidiError::WARNING, errorString_ );
|
error( RtMidiError::WARNING, errorString_ );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
snd_seq_drain_output( data->seq );
|
snd_seq_drain_output( data->seq );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2386,9 +2540,6 @@ static std::string ConvertToUTF8(const TCHAR *str)
|
|||||||
return u8str;
|
return u8str;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RT_SYSEX_BUFFER_SIZE 1024
|
|
||||||
#define RT_SYSEX_BUFFER_COUNT 4
|
|
||||||
|
|
||||||
// A structure to hold variables related to the CoreMIDI API
|
// A structure to hold variables related to the CoreMIDI API
|
||||||
// implementation.
|
// implementation.
|
||||||
struct WinMidiData {
|
struct WinMidiData {
|
||||||
@@ -2396,7 +2547,7 @@ struct WinMidiData {
|
|||||||
HMIDIOUT outHandle; // Handle to Midi Output Device
|
HMIDIOUT outHandle; // Handle to Midi Output Device
|
||||||
DWORD lastTime;
|
DWORD lastTime;
|
||||||
MidiInApi::MidiMessage message;
|
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
|
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.
|
// 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] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
|
||||||
data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
|
data->sysexBuffer[i]->lpData = new char[ inputData_.bufferSize ];
|
||||||
data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
|
data->sysexBuffer[i]->dwBufferLength = inputData_.bufferSize;
|
||||||
data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
|
data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
|
||||||
data->sysexBuffer[i]->dwFlags = 0;
|
data->sysexBuffer[i]->dwFlags = 0;
|
||||||
|
|
||||||
@@ -2630,7 +2782,7 @@ void MidiInWinMM :: closePort( void )
|
|||||||
midiInReset( data->inHandle );
|
midiInReset( data->inHandle );
|
||||||
midiInStop( 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));
|
int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
|
||||||
delete [] data->sysexBuffer[i]->lpData;
|
delete [] data->sysexBuffer[i]->lpData;
|
||||||
delete [] data->sysexBuffer[i];
|
delete [] data->sysexBuffer[i];
|
||||||
@@ -2811,7 +2963,10 @@ void MidiOutWinMM :: closePort( void )
|
|||||||
{
|
{
|
||||||
if ( connected_ ) {
|
if ( connected_ ) {
|
||||||
WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
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 );
|
midiOutClose( data->outHandle );
|
||||||
data->outHandle = 0;
|
data->outHandle = 0;
|
||||||
connected_ = false;
|
connected_ = false;
|
||||||
@@ -2936,6 +3091,7 @@ void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size )
|
|||||||
#include <jack/jack.h>
|
#include <jack/jack.h>
|
||||||
#include <jack/midiport.h>
|
#include <jack/midiport.h>
|
||||||
#include <jack/ringbuffer.h>
|
#include <jack/ringbuffer.h>
|
||||||
|
#include <pthread.h>
|
||||||
#ifdef HAVE_SEMAPHORE
|
#ifdef HAVE_SEMAPHORE
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -2945,8 +3101,8 @@ void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size )
|
|||||||
struct JackMidiData {
|
struct JackMidiData {
|
||||||
jack_client_t *client;
|
jack_client_t *client;
|
||||||
jack_port_t *port;
|
jack_port_t *port;
|
||||||
jack_ringbuffer_t *buffSize;
|
jack_ringbuffer_t *buff;
|
||||||
jack_ringbuffer_t *buffMessage;
|
int buffMaxWrite; // actual writable size, usually 1 less than ringbuffer
|
||||||
jack_time_t lastTime;
|
jack_time_t lastTime;
|
||||||
#ifdef HAVE_SEMAPHORE
|
#ifdef HAVE_SEMAPHORE
|
||||||
sem_t sem_cleanup;
|
sem_t sem_cleanup;
|
||||||
@@ -3101,6 +3257,8 @@ void MidiInJack :: openPort( unsigned int portNumber, const std::string &portNam
|
|||||||
|
|
||||||
if ( data->port == NULL ) {
|
if ( data->port == NULL ) {
|
||||||
errorString_ = "MidiInJack::openPort: JACK error creating port";
|
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_ );
|
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3123,6 +3281,8 @@ void MidiInJack :: openVirtualPort( const std::string &portName )
|
|||||||
|
|
||||||
if ( data->port == NULL ) {
|
if ( data->port == NULL ) {
|
||||||
errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";
|
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_ );
|
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 );
|
void *buff = jack_port_get_buffer( data->port, nframes );
|
||||||
jack_midi_clear_buffer( buff );
|
jack_midi_clear_buffer( buff );
|
||||||
|
|
||||||
while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
|
while ( jack_ringbuffer_peek( data->buff, (char *) &space, sizeof( space ) ) == sizeof(space) &&
|
||||||
jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof( space ) );
|
jack_ringbuffer_read_space( data->buff ) >= sizeof(space) + space ) {
|
||||||
midiData = jack_midi_event_reserve( buff, 0, 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
|
#ifdef HAVE_SEMAPHORE
|
||||||
@@ -3269,8 +3433,8 @@ void MidiOutJack :: connect()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Initialize output ringbuffers
|
// Initialize output ringbuffers
|
||||||
data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
|
data->buff = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
|
||||||
data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
|
data->buffMaxWrite = (int) jack_ringbuffer_write_space( data->buff );
|
||||||
|
|
||||||
// Initialize JACK client
|
// Initialize JACK client
|
||||||
if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) {
|
if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) {
|
||||||
@@ -3289,8 +3453,7 @@ MidiOutJack :: ~MidiOutJack()
|
|||||||
MidiOutJack::closePort();
|
MidiOutJack::closePort();
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
jack_ringbuffer_free( data->buffSize );
|
jack_ringbuffer_free( data->buff );
|
||||||
jack_ringbuffer_free( data->buffMessage );
|
|
||||||
if ( data->client ) {
|
if ( data->client ) {
|
||||||
jack_client_close( 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 ) {
|
if ( data->port == NULL ) {
|
||||||
errorString_ = "MidiOutJack::openPort: JACK error creating port";
|
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_ );
|
error( RtMidiError::DRIVER_ERROR, errorString_ );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3338,6 +3503,8 @@ void MidiOutJack :: openVirtualPort( const std::string &portName )
|
|||||||
|
|
||||||
if ( data->port == NULL ) {
|
if ( data->port == NULL ) {
|
||||||
errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";
|
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_ );
|
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);
|
int nBytes = static_cast<int>(size);
|
||||||
JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
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
|
// Write full message to buffer
|
||||||
jack_ringbuffer_write( data->buffMessage, ( const char * ) message, nBytes );
|
jack_ringbuffer_write( data->buff, ( char * ) &nBytes, sizeof( nBytes ) );
|
||||||
jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
|
jack_ringbuffer_write( data->buff, ( const char * ) message, nBytes );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __UNIX_JACK__
|
#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__
|
||||||
|
|||||||
Reference in New Issue
Block a user