mirror of
https://github.com/thestk/stk
synced 2026-01-11 12:01: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
|
||||
#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
|
||||
|
||||
@@ -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 ); }
|
||||
|
||||
755
src/RtAudio.cpp
755
src/RtAudio.cpp
File diff suppressed because it is too large
Load Diff
609
src/RtMidi.cpp
609
src/RtMidi.cpp
@@ -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__
|
||||
|
||||
Reference in New Issue
Block a user