mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-27 15:03:01 +00:00
373 lines
9.9 KiB
C++
373 lines
9.9 KiB
C++
|
#include <string.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include "rtmidi_c.h"
|
|||
|
#include "RtMidi.h"
|
|||
|
|
|||
|
/* Compile-time assertions that will break if the enums are changed in
|
|||
|
* the future without synchronizing them properly. If you get (g++)
|
|||
|
* "error: ‘StaticAssert<b>::StaticAssert() [with bool b = false]’ is
|
|||
|
* private within this context", it means enums are not aligned. */
|
|||
|
template<bool b> class StaticAssert { private: StaticAssert() {} };
|
|||
|
template<> class StaticAssert<true>{ public: StaticAssert() {} };
|
|||
|
#define ENUM_EQUAL(x,y) StaticAssert<(int)x==(int)y>()
|
|||
|
class StaticAssertions { StaticAssertions() {
|
|||
|
ENUM_EQUAL( RTMIDI_API_UNSPECIFIED, RtMidi::UNSPECIFIED );
|
|||
|
ENUM_EQUAL( RTMIDI_API_MACOSX_CORE, RtMidi::MACOSX_CORE );
|
|||
|
ENUM_EQUAL( RTMIDI_API_LINUX_ALSA, RtMidi::LINUX_ALSA );
|
|||
|
ENUM_EQUAL( RTMIDI_API_UNIX_JACK, RtMidi::UNIX_JACK );
|
|||
|
ENUM_EQUAL( RTMIDI_API_WINDOWS_MM, RtMidi::WINDOWS_MM );
|
|||
|
ENUM_EQUAL( RTMIDI_API_RTMIDI_DUMMY, RtMidi::RTMIDI_DUMMY );
|
|||
|
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_WARNING, RtMidiError::WARNING );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_DEBUG_WARNING, RtMidiError::DEBUG_WARNING );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_UNSPECIFIED, RtMidiError::UNSPECIFIED );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_NO_DEVICES_FOUND, RtMidiError::NO_DEVICES_FOUND );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_INVALID_DEVICE, RtMidiError::INVALID_DEVICE );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_MEMORY_ERROR, RtMidiError::MEMORY_ERROR );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_INVALID_PARAMETER, RtMidiError::INVALID_PARAMETER );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_INVALID_USE, RtMidiError::INVALID_USE );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_DRIVER_ERROR, RtMidiError::DRIVER_ERROR );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_SYSTEM_ERROR, RtMidiError::SYSTEM_ERROR );
|
|||
|
ENUM_EQUAL( RTMIDI_ERROR_THREAD_ERROR, RtMidiError::THREAD_ERROR );
|
|||
|
}};
|
|||
|
|
|||
|
class CallbackProxyUserData
|
|||
|
{
|
|||
|
public:
|
|||
|
CallbackProxyUserData (RtMidiCCallback cCallback, void *userData)
|
|||
|
: c_callback (cCallback), user_data (userData)
|
|||
|
{
|
|||
|
}
|
|||
|
RtMidiCCallback c_callback;
|
|||
|
void *user_data;
|
|||
|
};
|
|||
|
|
|||
|
extern "C" const enum RtMidiApi rtmidi_compiled_apis[]; // casting from RtMidi::Api[]
|
|||
|
extern "C" const unsigned int rtmidi_num_compiled_apis;
|
|||
|
|
|||
|
/* RtMidi API */
|
|||
|
int rtmidi_get_compiled_api (enum RtMidiApi *apis, unsigned int apis_size)
|
|||
|
{
|
|||
|
unsigned num = rtmidi_num_compiled_apis;
|
|||
|
if (apis) {
|
|||
|
num = (num < apis_size) ? num : apis_size;
|
|||
|
memcpy(apis, rtmidi_compiled_apis, num * sizeof(enum RtMidiApi));
|
|||
|
}
|
|||
|
return (int)num;
|
|||
|
}
|
|||
|
|
|||
|
extern "C" const char* rtmidi_api_names[][2];
|
|||
|
const char *rtmidi_api_name(enum RtMidiApi api) {
|
|||
|
if (api < 0 || api >= RTMIDI_API_NUM)
|
|||
|
return NULL;
|
|||
|
return rtmidi_api_names[api][0];
|
|||
|
}
|
|||
|
|
|||
|
const char *rtmidi_api_display_name(enum RtMidiApi api)
|
|||
|
{
|
|||
|
if (api < 0 || api >= RTMIDI_API_NUM)
|
|||
|
return "Unknown";
|
|||
|
return rtmidi_api_names[api][1];
|
|||
|
}
|
|||
|
|
|||
|
enum RtMidiApi rtmidi_compiled_api_by_name(const char *name) {
|
|||
|
RtMidi::Api api = RtMidi::UNSPECIFIED;
|
|||
|
if (name) {
|
|||
|
api = RtMidi::getCompiledApiByName(name);
|
|||
|
}
|
|||
|
return (enum RtMidiApi)api;
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_error (MidiApi *api, enum RtMidiErrorType type, const char* errorString)
|
|||
|
{
|
|||
|
std::string msg = errorString;
|
|||
|
api->error ((RtMidiError::Type) type, msg);
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_open_port (RtMidiPtr device, unsigned int portNumber, const char *portName)
|
|||
|
{
|
|||
|
std::string name = portName;
|
|||
|
try {
|
|||
|
((RtMidi*) device->ptr)->openPort (portNumber, name);
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_open_virtual_port (RtMidiPtr device, const char *portName)
|
|||
|
{
|
|||
|
std::string name = portName;
|
|||
|
try {
|
|||
|
((RtMidi*) device->ptr)->openVirtualPort (name);
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_close_port (RtMidiPtr device)
|
|||
|
{
|
|||
|
try {
|
|||
|
((RtMidi*) device->ptr)->closePort ();
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
unsigned int rtmidi_get_port_count (RtMidiPtr device)
|
|||
|
{
|
|||
|
try {
|
|||
|
return ((RtMidi*) device->ptr)->getPortCount ();
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int rtmidi_get_port_name (RtMidiPtr device, unsigned int portNumber, char * bufOut, int * bufLen)
|
|||
|
{
|
|||
|
if (bufOut == nullptr && bufLen == nullptr) {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
std::string name;
|
|||
|
try {
|
|||
|
name = ((RtMidi*) device->ptr)->getPortName (portNumber);
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if (bufOut == nullptr) {
|
|||
|
*bufLen = static_cast<int>(name.size()) + 1;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
return snprintf(bufOut, static_cast<size_t>(*bufLen), "%s", name.c_str());
|
|||
|
}
|
|||
|
|
|||
|
/* RtMidiIn API */
|
|||
|
RtMidiInPtr rtmidi_in_create_default ()
|
|||
|
{
|
|||
|
RtMidiWrapper* wrp = new RtMidiWrapper;
|
|||
|
|
|||
|
try {
|
|||
|
RtMidiIn* rIn = new RtMidiIn ();
|
|||
|
|
|||
|
wrp->ptr = (void*) rIn;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = true;
|
|||
|
wrp->msg = "";
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
wrp->ptr = 0;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = false;
|
|||
|
wrp->msg = err.what ();
|
|||
|
}
|
|||
|
|
|||
|
return wrp;
|
|||
|
}
|
|||
|
|
|||
|
RtMidiInPtr rtmidi_in_create (enum RtMidiApi api, const char *clientName, unsigned int queueSizeLimit)
|
|||
|
{
|
|||
|
std::string name = clientName;
|
|||
|
RtMidiWrapper* wrp = new RtMidiWrapper;
|
|||
|
|
|||
|
try {
|
|||
|
RtMidiIn* rIn = new RtMidiIn ((RtMidi::Api) api, name, queueSizeLimit);
|
|||
|
|
|||
|
wrp->ptr = (void*) rIn;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = true;
|
|||
|
wrp->msg = "";
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
wrp->ptr = 0;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = false;
|
|||
|
wrp->msg = err.what ();
|
|||
|
}
|
|||
|
|
|||
|
return wrp;
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_in_free (RtMidiInPtr device)
|
|||
|
{
|
|||
|
if (device->data)
|
|||
|
delete (CallbackProxyUserData*) device->data;
|
|||
|
delete (RtMidiIn*) device->ptr;
|
|||
|
delete device;
|
|||
|
}
|
|||
|
|
|||
|
enum RtMidiApi rtmidi_in_get_current_api (RtMidiPtr device)
|
|||
|
{
|
|||
|
try {
|
|||
|
return (RtMidiApi) ((RtMidiIn*) device->ptr)->getCurrentApi ();
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
|
|||
|
return RTMIDI_API_UNSPECIFIED;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
void callback_proxy (double timeStamp, std::vector<unsigned char> *message, void *userData)
|
|||
|
{
|
|||
|
CallbackProxyUserData* data = reinterpret_cast<CallbackProxyUserData*> (userData);
|
|||
|
data->c_callback (timeStamp, message->data (), message->size (), data->user_data);
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_in_set_callback (RtMidiInPtr device, RtMidiCCallback callback, void *userData)
|
|||
|
{
|
|||
|
device->data = (void*) new CallbackProxyUserData (callback, userData);
|
|||
|
try {
|
|||
|
((RtMidiIn*) device->ptr)->setCallback (callback_proxy, device->data);
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
delete (CallbackProxyUserData*) device->data;
|
|||
|
device->data = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_in_cancel_callback (RtMidiInPtr device)
|
|||
|
{
|
|||
|
try {
|
|||
|
((RtMidiIn*) device->ptr)->cancelCallback ();
|
|||
|
delete (CallbackProxyUserData*) device->data;
|
|||
|
device->data = 0;
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_in_ignore_types (RtMidiInPtr device, bool midiSysex, bool midiTime, bool midiSense)
|
|||
|
{
|
|||
|
((RtMidiIn*) device->ptr)->ignoreTypes (midiSysex, midiTime, midiSense);
|
|||
|
}
|
|||
|
|
|||
|
double rtmidi_in_get_message (RtMidiInPtr device,
|
|||
|
unsigned char *message,
|
|||
|
size_t *size)
|
|||
|
{
|
|||
|
try {
|
|||
|
// FIXME: use allocator to achieve efficient buffering
|
|||
|
std::vector<unsigned char> v;
|
|||
|
double ret = ((RtMidiIn*) device->ptr)->getMessage (&v);
|
|||
|
|
|||
|
if (v.size () > 0 && v.size() <= *size) {
|
|||
|
memcpy (message, v.data (), (int) v.size ());
|
|||
|
}
|
|||
|
|
|||
|
*size = v.size();
|
|||
|
return ret;
|
|||
|
}
|
|||
|
catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
return -1;
|
|||
|
}
|
|||
|
catch (...) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = "Unknown error";
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* RtMidiOut API */
|
|||
|
RtMidiOutPtr rtmidi_out_create_default ()
|
|||
|
{
|
|||
|
RtMidiWrapper* wrp = new RtMidiWrapper;
|
|||
|
|
|||
|
try {
|
|||
|
RtMidiOut* rOut = new RtMidiOut ();
|
|||
|
|
|||
|
wrp->ptr = (void*) rOut;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = true;
|
|||
|
wrp->msg = "";
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
wrp->ptr = 0;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = false;
|
|||
|
wrp->msg = err.what ();
|
|||
|
}
|
|||
|
|
|||
|
return wrp;
|
|||
|
}
|
|||
|
|
|||
|
RtMidiOutPtr rtmidi_out_create (enum RtMidiApi api, const char *clientName)
|
|||
|
{
|
|||
|
RtMidiWrapper* wrp = new RtMidiWrapper;
|
|||
|
std::string name = clientName;
|
|||
|
|
|||
|
try {
|
|||
|
RtMidiOut* rOut = new RtMidiOut ((RtMidi::Api) api, name);
|
|||
|
|
|||
|
wrp->ptr = (void*) rOut;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = true;
|
|||
|
wrp->msg = "";
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
wrp->ptr = 0;
|
|||
|
wrp->data = 0;
|
|||
|
wrp->ok = false;
|
|||
|
wrp->msg = err.what ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return wrp;
|
|||
|
}
|
|||
|
|
|||
|
void rtmidi_out_free (RtMidiOutPtr device)
|
|||
|
{
|
|||
|
delete (RtMidiOut*) device->ptr;
|
|||
|
delete device;
|
|||
|
}
|
|||
|
|
|||
|
enum RtMidiApi rtmidi_out_get_current_api (RtMidiPtr device)
|
|||
|
{
|
|||
|
try {
|
|||
|
return (RtMidiApi) ((RtMidiOut*) device->ptr)->getCurrentApi ();
|
|||
|
|
|||
|
} catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
|
|||
|
return RTMIDI_API_UNSPECIFIED;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int rtmidi_out_send_message (RtMidiOutPtr device, const unsigned char *message, int length)
|
|||
|
{
|
|||
|
try {
|
|||
|
((RtMidiOut*) device->ptr)->sendMessage (message, length);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
catch (const RtMidiError & err) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = err.what ();
|
|||
|
return -1;
|
|||
|
}
|
|||
|
catch (...) {
|
|||
|
device->ok = false;
|
|||
|
device->msg = "Unknown error";
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|