summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/portmidi/pm_common/portmidi.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/portmidi/pm_common/portmidi.c')
-rw-r--r--3rdparty/portmidi/pm_common/portmidi.c720
1 files changed, 492 insertions, 228 deletions
diff --git a/3rdparty/portmidi/pm_common/portmidi.c b/3rdparty/portmidi/pm_common/portmidi.c
index 2ed9171e781..7c906dcae42 100644
--- a/3rdparty/portmidi/pm_common/portmidi.c
+++ b/3rdparty/portmidi/pm_common/portmidi.c
@@ -1,7 +1,5 @@
-#ifdef _MSC_VER
- #pragma warning(disable: 4244) // stop warnings about downsize typecasts
- #pragma warning(disable: 4018) // stop warnings about signed/unsigned
-#endif
+/* portmidi.c -- cross-platform MIDI I/O library */
+/* see license.txt for license */
#include "stdlib.h"
#include "string.h"
@@ -36,12 +34,21 @@
#define is_empty(midi) ((midi)->tail == (midi)->head)
-/* this is not static so that pm_init can set it directly if
+/* this is not static so that pm_init can set it directly
* (see pmmac.c:pm_init())
*/
int pm_initialized = FALSE;
-int pm_hosterror;
+int pm_hosterror; /* boolean */
+
+/* if PM_CHECK_ERRORS is enabled, but the caller wants to
+ * handle an error condition, declare this as extern and
+ * set to FALSE (this override is provided specifically
+ * for the test program virttest.c, where pmNameConflict
+ * is expected in a call to Pm_CreateVirtualInput()):
+ */
+int pm_check_errors = TRUE;
+
char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
#ifdef PM_CHECK_ERRORS
@@ -54,15 +61,16 @@ static void prompt_and_exit(void)
{
char line[STRING_MAX];
printf("type ENTER...");
- fgets(line, STRING_MAX, stdin);
+ char *rslt = fgets(line, STRING_MAX, stdin);
/* this will clean up open ports: */
exit(-1);
}
-
static PmError pm_errmsg(PmError err)
{
- if (err == pmHostError) {
+ if (!pm_check_errors) { /* see pm_check_errors declaration above */
+ ;
+ } else if (err == pmHostError) {
/* it seems pointless to allocate memory and copy the string,
* so I will do the work of Pm_GetHostErrorText directly
*/
@@ -87,52 +95,193 @@ system implementation of portmidi interface
*/
int pm_descriptor_max = 0;
-int pm_descriptor_index = 0;
-descriptor_type descriptors = NULL;
+int pm_descriptor_len = 0;
+descriptor_type pm_descriptors = NULL;
+
+/* interface pm_descriptors are simple: an array of string/fnptr pairs: */
+#define MAX_INTERF 4
+static struct {
+ const char *interf;
+ pm_create_fn create_fn;
+ pm_delete_fn delete_fn;
+} pm_interf_list[MAX_INTERF];
+
+static int pm_interf_list_len = 0;
+
+
+/* pm_add_interf -- describe an interface to library
+ *
+ * This is called at initialization time, once for each
+ * supported interface (e.g., CoreMIDI). The strings
+ * are retained but NOT COPIED, so do not destroy them!
+ *
+ * The purpose is to register functions that create/delete
+ * a virtual input or output device.
+ *
+ * returns pmInsufficientMemor if interface memory is
+ * exceeded, otherwise returns pmNoError.
+ */
+PmError pm_add_interf(const char *interf, pm_create_fn create_fn,
+ pm_delete_fn delete_fn)
+{
+ if (pm_interf_list_len >= MAX_INTERF) {
+ return pmInsufficientMemory;
+ }
+ pm_interf_list[pm_interf_list_len].interf = interf;
+ pm_interf_list[pm_interf_list_len].create_fn = create_fn;
+ pm_interf_list[pm_interf_list_len].delete_fn = delete_fn;
+ pm_interf_list_len++;
+ return pmNoError;
+}
+
+
+PmError pm_create_virtual(PmInternal *midi, int is_input, const char *interf,
+ const char *name, void *device_info)
+{
+ int i;
+ if (pm_interf_list_len == 0) {
+ return pmNotImplemented;
+ }
+ if (!interf) {
+ /* default interface is the first one */
+ interf = pm_interf_list[0].interf;
+ }
+ for (i = 0; i < pm_interf_list_len; i++) {
+ if (strcmp(pm_interf_list[i].interf,
+ interf) == 0) {
+ int id = (*pm_interf_list[i].create_fn)(is_input, name,
+ device_info);
+ pm_descriptors[id].pub.is_virtual = TRUE;
+ return id;
+ }
+ }
+ return pmInterfaceNotSupported;
+}
+
/* pm_add_device -- describe interface/device pair to library
*
* This is called at intialization time, once for each
- * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1)
- * The strings are retained but NOT COPIED, so do not destroy them!
+ * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1).
+ * This is also called when user creates a virtual device.
+ *
+ * Normally, increasing integer indices are returned. If the device
+ * is virtual, a linear search is performed to ensure that the name
+ * is unique. If the name is already taken, the call will fail and
+ * no device is added.
+ *
+ * interf is assumed to be static memory, so it is NOT COPIED and
+ * NOT FREED.
+ * name is owned by caller, COPIED if needed, and FREED by PortMidi.
+ * Caller is resposible for freeing name when pm_add_device returns.
*
- * returns pmInvalidDeviceId if device memory is exceeded
- * otherwise returns pmNoError
+ * returns pmInvalidDeviceId if device memory is exceeded or a virtual
+ * device would take the name of an existing device.
+ * otherwise returns index (portmidi device_id) of the added device
*/
-PmError pm_add_device(char *interf, char *name, int input,
- void *descriptor, pm_fns_type dictionary) {
- if (pm_descriptor_index >= pm_descriptor_max) {
- // expand descriptors
+PmError pm_add_device(const char *interf, const char *name, int is_input,
+ int is_virtual, void *descriptor, pm_fns_type dictionary) {
+ /* printf("pm_add_device: %s %s %d %p %p\n",
+ interf, name, is_input, descriptor, dictionary); */
+ int device_id;
+ PmDeviceInfo *d;
+ /* if virtual, search for duplicate name or unused ID; otherwise,
+ * just add a new device at the next integer available:
+ */
+ for (device_id = (is_virtual ? 0 : pm_descriptor_len);
+ device_id < pm_descriptor_len; device_id++) {
+ d = &pm_descriptors[device_id].pub;
+ d->structVersion = 200;
+ if (strcmp(d->interf, interf) == 0 && strcmp(d->name, name) == 0) {
+ /* only reuse a name if it is a deleted virtual device with
+ * a matching direction (input or output) */
+ if (pm_descriptors[device_id].deleted && is_input == d->input) {
+ /* here, we know d->is_virtual because only virtual devices
+ * can be deleted, and we know is_virtual because we are
+ * in this loop.
+ */
+ pm_free((void *) d->name); /* reuse this device entry */
+ d->name = NULL;
+ break;
+ /* name conflict exists if the new device appears to others as
+ * the same direction (input or output) as the existing device.
+ * Note that virtual inputs appear to others as outputs and
+ * vice versa.
+ * The direction of the new virtual device to others is "output"
+ * if is_input, i.e., virtual inputs appear to others as outputs.
+ * The existing device appears to others as "output" if
+ * (d->is_virtual == d->input) by the same logic.
+ * The compare will detect if device directions are the same:
+ */
+ } else if (is_input == (d->is_virtual == d->input)) {
+ return pmNameConflict;
+ }
+ }
+ }
+ if (device_id >= pm_descriptor_max) {
+ // expand pm_descriptors
descriptor_type new_descriptors = (descriptor_type)
pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32));
if (!new_descriptors) return pmInsufficientMemory;
- if (descriptors) {
- memcpy(new_descriptors, descriptors,
+ if (pm_descriptors) {
+ memcpy(new_descriptors, pm_descriptors,
sizeof(descriptor_node) * pm_descriptor_max);
- free(descriptors);
+ pm_free(pm_descriptors);
}
pm_descriptor_max += 32;
- descriptors = new_descriptors;
+ pm_descriptors = new_descriptors;
+ }
+ if (device_id == pm_descriptor_len) {
+ pm_descriptor_len++; /* extending array of pm_descriptors */
}
- descriptors[pm_descriptor_index].pub.interf = interf;
- descriptors[pm_descriptor_index].pub.name = name;
- descriptors[pm_descriptor_index].pub.input = input;
- descriptors[pm_descriptor_index].pub.output = !input;
+ d = &pm_descriptors[device_id].pub;
+ d->interf = interf;
+ d->name = pm_alloc(strlen(name) + 1);
+ if (!d->name) {
+ return pmInsufficientMemory;
+ }
+#pragma warning(suppress: 4996) // don't use suggested strncpy_s
+ strcpy(d->name, name);
+ d->input = is_input;
+ d->output = !is_input;
+ d->is_virtual = FALSE; /* caller should set to TRUE if this is virtual */
/* default state: nothing to close (for automatic device closing) */
- descriptors[pm_descriptor_index].pub.opened = FALSE;
+ d->opened = FALSE;
+
+ pm_descriptors[device_id].deleted = FALSE;
/* ID number passed to win32 multimedia API open */
- descriptors[pm_descriptor_index].descriptor = descriptor;
+ pm_descriptors[device_id].descriptor = descriptor;
/* points to PmInternal, allows automatic device closing */
- descriptors[pm_descriptor_index].internalDescriptor = NULL;
+ pm_descriptors[device_id].pm_internal = NULL;
- descriptors[pm_descriptor_index].dictionary = dictionary;
-
- pm_descriptor_index++;
+ pm_descriptors[device_id].dictionary = dictionary;
- return pmNoError;
+ return device_id;
+}
+
+
+/* Undo a successful call to pm_add_device(). If a new device was
+ * allocated, it must be the last device in pm_descriptors, so it is
+ * easy to delete by decrementing the length of pm_descriptors, but
+ * first free the name (which was copied to the heap). Otherwise,
+ * the device must be a virtual device that was created previously
+ * and is in the interior of the array of pm_descriptors. Leave it,
+ * but mark it as deleted.
+ */
+void pm_undo_add_device(int id)
+{
+ /* Clear some fields (not all are strictly necessary) */
+ pm_descriptors[id].deleted = TRUE;
+ pm_descriptors[id].descriptor = NULL;
+ pm_descriptors[id].pm_internal = NULL;
+
+ if (id == pm_descriptor_len - 1) {
+ pm_free(pm_descriptors[id].pub.name);
+ pm_descriptor_len--;
+ }
}
@@ -144,7 +293,7 @@ int pm_find_default_device(char *pattern, int is_input)
int id = pmNoDevice;
int i;
/* first parse pattern into name, interf parts */
- char *interf_pref = (char *)""; /* initially assume it is not there */
+ char const *interf_pref = ""; /* initially assume it is not there */
char *name_pref = strstr(pattern, ", ");
if (name_pref) { /* found separator, adjust the pointer */
@@ -154,7 +303,7 @@ int pm_find_default_device(char *pattern, int is_input)
} else {
name_pref = pattern; /* whole string is the name pattern */
}
- for (i = 0; i < pm_descriptor_index; i++) {
+ for (i = 0; i < pm_descriptor_len; i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
if (info->input == is_input &&
strstr(info->name, name_pref) &&
@@ -173,56 +322,71 @@ portmidi implementation
====================================================================
*/
-PMEXPORT int Pm_CountDevices( void ) {
+PMEXPORT int Pm_CountDevices(void)
+{
Pm_Initialize();
/* no error checking -- Pm_Initialize() does not fail */
- return pm_descriptor_index;
+ return pm_descriptor_len;
}
-PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) {
+PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo(PmDeviceID id)
+{
Pm_Initialize(); /* no error check needed */
- if (id >= 0 && id < pm_descriptor_index) {
- return &descriptors[id].pub;
+ if (id >= 0 && id < pm_descriptor_len && !pm_descriptors[id].deleted) {
+ return &pm_descriptors[id].pub;
}
return NULL;
}
/* pm_success_fn -- "noop" function pointer */
-PmError pm_success_fn(PmInternal *midi) {
+PmError pm_success_fn(PmInternal *midi)
+{
return pmNoError;
}
/* none_write -- returns an error if called */
-PmError none_write_short(PmInternal *midi, PmEvent *buffer) {
+PmError none_write_short(PmInternal *midi, PmEvent *buffer)
+{
return pmBadPtr;
}
/* pm_fail_timestamp_fn -- placeholder for begin_sysex and flush */
-PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) {
+PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp)
+{
return pmBadPtr;
}
PmError none_write_byte(PmInternal *midi, unsigned char byte,
- PmTimestamp timestamp) {
+ PmTimestamp timestamp)
+{
return pmBadPtr;
}
/* pm_fail_fn -- generic function, returns error if called */
-PmError pm_fail_fn(PmInternal *midi) {
+PmError pm_fail_fn(PmInternal *midi)
+{
return pmBadPtr;
}
-static PmError none_open(PmInternal *midi, void *driverInfo) {
+static PmError none_open(PmInternal *midi, void *driverInfo)
+{
return pmBadPtr;
}
-static void none_get_host_error(PmInternal * midi, char * msg, unsigned int len) {
+
+static void none_get_host_error(PmInternal * midi, char * msg,
+ unsigned int len)
+{
*msg = 0; // empty string
}
-static unsigned int none_has_host_error(PmInternal * midi) {
+
+static unsigned int none_check_host_error(PmInternal * midi)
+{
return FALSE;
}
-PmTimestamp none_synchronize(PmInternal *midi) {
+
+PmTimestamp none_synchronize(PmInternal *midi)
+{
return 0;
}
@@ -238,15 +402,15 @@ pm_fns_node pm_none_dictionary = {
none_write_flush,
none_synchronize,
none_open,
- none_abort,
+ none_abort,
none_close,
none_poll,
- none_has_host_error,
- none_get_host_error
+ none_check_host_error,
};
-PMEXPORT const char *Pm_GetErrorText( PmError errnum ) {
+PMEXPORT const char *Pm_GetErrorText(PmError errnum)
+{
const char *msg;
switch(errnum)
@@ -255,47 +419,59 @@ PMEXPORT const char *Pm_GetErrorText( PmError errnum ) {
msg = "";
break;
case pmHostError:
- msg = "PortMidi: `Host error'";
+ msg = "PortMidi: Host error";
break;
case pmInvalidDeviceId:
- msg = "PortMidi: `Invalid device ID'";
+ msg = "PortMidi: Invalid device ID";
break;
case pmInsufficientMemory:
- msg = "PortMidi: `Insufficient memory'";
+ msg = "PortMidi: Insufficient memory";
break;
case pmBufferTooSmall:
- msg = "PortMidi: `Buffer too small'";
+ msg = "PortMidi: Buffer too small";
break;
case pmBadPtr:
- msg = "PortMidi: `Bad pointer'";
+ msg = "PortMidi: Bad pointer";
break;
case pmInternalError:
- msg = "PortMidi: `Internal PortMidi Error'";
+ msg = "PortMidi: Internal PortMidi Error";
break;
case pmBufferOverflow:
- msg = "PortMidi: `Buffer overflow'";
+ msg = "PortMidi: Buffer overflow";
break;
case pmBadData:
- msg = "PortMidi: `Invalid MIDI message Data'";
+ msg = "PortMidi: Invalid MIDI message Data";
break;
case pmBufferMaxSize:
- msg = "PortMidi: `Buffer cannot be made larger'";
+ msg = "PortMidi: Buffer cannot be made larger";
+ break;
+ case pmNotImplemented:
+ msg = "PortMidi: Function is not implemented";
+ break;
+ case pmInterfaceNotSupported:
+ msg = "PortMidi: Interface not supported";
break;
- default:
- msg = "PortMidi: `Illegal error number'";
+ case pmNameConflict:
+ msg = "PortMidi: Cannot create virtual device: name is taken";
+ break;
+ default:
+ msg = "PortMidi: Illegal error number";
break;
}
return msg;
}
-/* This can be called whenever you get a pmHostError return value.
+/* This can be called whenever you get a pmHostError return value
+ * or TRUE from Pm_HasHostError().
* The error will always be in the global pm_hosterror_text.
*/
-PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) {
+PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len)
+{
assert(msg);
assert(len > 0);
if (pm_hosterror) {
+#pragma warning(suppress: 4996) // don't use suggested strncpy_s
strncpy(msg, (char *) pm_hosterror_text, len);
pm_hosterror = FALSE;
pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it
@@ -307,24 +483,22 @@ PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) {
}
-PMEXPORT int Pm_HasHostError(PortMidiStream * stream) {
+PMEXPORT int Pm_HasHostError(PortMidiStream * stream)
+{
if (pm_hosterror) return TRUE;
if (stream) {
PmInternal * midi = (PmInternal *) stream;
- pm_hosterror = (*midi->dictionary->has_host_error)(midi);
- if (pm_hosterror) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- /* now error message is global */
- return TRUE;
- }
+ return (*midi->dictionary->check_host_error)(midi);
}
return FALSE;
}
-PMEXPORT PmError Pm_Initialize( void ) {
+PMEXPORT PmError Pm_Initialize(void)
+{
if (!pm_initialized) {
+ pm_descriptor_len = 0;
+ pm_interf_list_len = 0;
pm_hosterror = FALSE;
pm_hosterror_text[0] = 0; /* the null string */
pm_init();
@@ -334,16 +508,24 @@ PMEXPORT PmError Pm_Initialize( void ) {
}
-PMEXPORT PmError Pm_Terminate( void ) {
+PMEXPORT PmError Pm_Terminate(void)
+{
if (pm_initialized) {
pm_term();
- // if there are no devices, descriptors might still be NULL
- if (descriptors != NULL) {
- free(descriptors);
- descriptors = NULL;
+ /* if there are no devices, pm_descriptors might still be NULL */
+ if (pm_descriptors != NULL) {
+ int i; /* free names copied into pm_descriptors */
+ for (i = 0; i < pm_descriptor_len; i++) {
+ if (pm_descriptors[i].pub.name) {
+ pm_free(pm_descriptors[i].pub.name);
+ }
+ }
+ pm_free(pm_descriptors);
+ pm_descriptors = NULL;
}
- pm_descriptor_index = 0;
+ pm_descriptor_len = 0;
pm_descriptor_max = 0;
+ pm_interf_list_len = 0;
pm_initialized = FALSE;
}
return pmNoError;
@@ -354,7 +536,8 @@ PMEXPORT PmError Pm_Terminate( void ) {
/*
* returns number of messages actually read, or error code
*/
-PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
+PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length)
+{
PmInternal *midi = (PmInternal *) stream;
int n = 0;
PmError err = pmNoError;
@@ -362,9 +545,9 @@ PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
/* arg checking */
if(midi == NULL)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.opened)
+ else if(!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.input)
+ else if(!pm_descriptors[midi->device_id].pub.input)
err = pmBadPtr;
/* First poll for data in the buffer...
* This either simply checks for data, or attempts first to fill the buffer
@@ -375,9 +558,7 @@ PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
if (err != pmNoError) {
if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
+ midi->dictionary->check_host_error(midi);
}
return pm_errmsg(err);
}
@@ -395,7 +576,7 @@ PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
return n;
}
-PMEXPORT PmError Pm_Poll( PortMidiStream *stream )
+PMEXPORT PmError Pm_Poll(PortMidiStream *stream)
{
PmInternal *midi = (PmInternal *) stream;
PmError err;
@@ -404,23 +585,18 @@ PMEXPORT PmError Pm_Poll( PortMidiStream *stream )
/* arg checking */
if(midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.input)
+ else if (!pm_descriptors[midi->device_id].pub.input)
err = pmBadPtr;
else
err = (*(midi->dictionary->poll))(midi);
if (err != pmNoError) {
- if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
- }
return pm_errmsg(err);
}
- return !Pm_QueueEmpty(midi->queue);
+ return (PmError) !Pm_QueueEmpty(midi->queue);
}
@@ -432,11 +608,6 @@ static PmError pm_end_sysex(PmInternal *midi)
{
PmError err = (*midi->dictionary->end_sysex)(midi, 0);
midi->sysex_in_progress = FALSE;
- if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
- }
return err;
}
@@ -445,7 +616,8 @@ static PmError pm_end_sysex(PmInternal *midi)
Pm_WriteSysEx all operate a state machine that "outputs" calls to
write_short, begin_sysex, write_byte, end_sysex, and write_realtime */
-PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length)
+PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer,
+ int32_t length)
{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
@@ -456,9 +628,9 @@ PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t leng
/* arg checking */
if(midi == NULL)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.opened)
+ else if(!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.output)
+ else if(!pm_descriptors[midi->device_id].pub.output)
err = pmBadPtr;
else
err = pmNoError;
@@ -569,16 +741,15 @@ PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t leng
err = (*midi->dictionary->write_flush)(midi, 0);
pm_write_error:
if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
+ midi->dictionary->check_host_error(midi);
}
error_exit:
return pm_errmsg(err);
}
-PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, PmMessage msg)
+PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when,
+ PmMessage msg)
{
PmEvent event;
@@ -666,79 +837,110 @@ end_of_sysex:
-PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream,
- PmDeviceID inputDevice,
- void *inputDriverInfo,
- int32_t bufferSize,
- PmTimeProcPtr time_proc,
- void *time_info)
+PmError pm_create_internal(PmInternal **stream, PmDeviceID device_id,
+ int is_input, int latency, PmTimeProcPtr time_proc,
+ void *time_info, int buffer_size)
{
PmInternal *midi;
- PmError err = pmNoError;
- pm_hosterror = FALSE;
- *stream = NULL;
-
- /* arg checking */
- if (inputDevice < 0 || inputDevice >= pm_descriptor_index)
- err = pmInvalidDeviceId;
- else if (!descriptors[inputDevice].pub.input)
- err = pmInvalidDeviceId;
- else if(descriptors[inputDevice].pub.opened)
- err = pmInvalidDeviceId;
-
- if (err != pmNoError)
- goto error_return;
-
+ if (device_id < 0 || device_id >= pm_descriptor_len) {
+ return pmInvalidDeviceId;
+ }
+ if (latency < 0) { /* force a legal value */
+ latency = 0;
+ }
/* create portMidi internal data */
midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
*stream = midi;
if (!midi) {
- err = pmInsufficientMemory;
- goto error_return;
+ return pmInsufficientMemory;
}
- midi->device_id = inputDevice;
- midi->write_flag = FALSE;
+ midi->device_id = device_id;
+ midi->is_input = is_input;
midi->time_proc = time_proc;
+ /* if latency != 0, we need a time reference for output.
+ we always need a time reference for input.
+ If none is provided, use PortTime library */
+ if (time_proc == NULL && (latency != 0 || is_input)) {
+ if (!Pt_Started())
+ Pt_Start(1, 0, 0);
+ /* time_get does not take a parameter, so coerce */
+ midi->time_proc = (PmTimeProcPtr) Pt_Time;
+ }
midi->time_info = time_info;
- /* windows adds timestamps in the driver and these are more accurate than
- using a time_proc, so do not automatically provide a time proc. Non-win
- implementations may want to provide a default time_proc in their
- system-specific midi_out_open() method.
- */
- if (bufferSize <= 0) bufferSize = 256; /* default buffer size */
- midi->queue = Pm_QueueCreate(bufferSize, (int32_t) sizeof(PmEvent));
- if (!midi->queue) {
- /* free portMidi data */
- *stream = NULL;
- pm_free(midi);
- err = pmInsufficientMemory;
- goto error_return;
+ if (is_input) {
+ midi->latency = 0; /* unused by input */
+ if (buffer_size <= 0) buffer_size = 256; /* default buffer size */
+ midi->queue = Pm_QueueCreate(buffer_size, (int32_t) sizeof(PmEvent));
+ if (!midi->queue) {
+ /* free portMidi data */
+ *stream = NULL;
+ pm_free(midi);
+ return pmInsufficientMemory;
+ }
+ } else {
+ /* if latency zero, output immediate (timestamps ignored) */
+ /* if latency < 0, use 0 but don't return an error */
+ if (latency < 0) latency = 0;
+ midi->latency = latency;
+ midi->queue = NULL; /* unused by output; input needs to allocate: */
}
- midi->buffer_len = bufferSize; /* portMidi input storage */
- midi->latency = 0; /* not used */
+ midi->buffer_len = buffer_size; /* portMidi input storage */
midi->sysex_in_progress = FALSE;
midi->sysex_message = 0;
midi->sysex_message_count = 0;
- midi->filters = PM_FILT_ACTIVE;
+ midi->filters = (is_input ? PM_FILT_ACTIVE : 0);
midi->channel_mask = 0xFFFF;
midi->sync_time = 0;
midi->first_message = TRUE;
- midi->dictionary = descriptors[inputDevice].dictionary;
+ midi->api_info = NULL;
midi->fill_base = NULL;
midi->fill_offset_ptr = NULL;
midi->fill_length = 0;
- descriptors[inputDevice].internalDescriptor = midi;
+ midi->dictionary = pm_descriptors[device_id].dictionary;
+ pm_descriptors[device_id].pm_internal = midi;
+ return pmNoError;
+}
+
+
+PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream,
+ PmDeviceID inputDevice,
+ void *inputDriverInfo,
+ int32_t bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info)
+{
+ PmInternal *midi = NULL;
+ PmError err = pmNoError;
+ pm_hosterror = FALSE;
+ *stream = NULL; /* invariant: *stream == midi */
+
+ /* arg checking */
+ if (!pm_descriptors[inputDevice].pub.input)
+ err = pmInvalidDeviceId;
+ else if (pm_descriptors[inputDevice].pub.opened)
+ err = pmInvalidDeviceId;
+ if (err != pmNoError)
+ goto error_return;
+
+ /* common initialization of PmInternal structure (midi): */
+ err = pm_create_internal(&midi, inputDevice, TRUE, 0, time_proc,
+ time_info, bufferSize);
+ *stream = midi;
+ if (err) {
+ goto error_return;
+ }
+
/* open system dependent input device */
err = (*midi->dictionary->open)(midi, inputDriverInfo);
if (err) {
*stream = NULL;
- descriptors[inputDevice].internalDescriptor = NULL;
+ pm_descriptors[inputDevice].pm_internal = NULL;
/* free portMidi data */
Pm_QueueDestroy(midi->queue);
pm_free(midi);
} else {
/* portMidi input open successful */
- descriptors[inputDevice].pub.opened = TRUE;
+ pm_descriptors[inputDevice].pub.opened = TRUE;
}
error_return:
/* note: if there is a pmHostError, it is the responsibility
@@ -750,12 +952,12 @@ error_return:
PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream,
- PmDeviceID outputDevice,
- void *outputDriverInfo,
- int32_t bufferSize,
- PmTimeProcPtr time_proc,
- void *time_info,
- int32_t latency )
+ PmDeviceID outputDevice,
+ void *outputDriverInfo,
+ int32_t bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ int32_t latency)
{
PmInternal *midi;
PmError err = pmNoError;
@@ -763,62 +965,33 @@ PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream,
*stream = NULL;
/* arg checking */
- if (outputDevice < 0 || outputDevice >= pm_descriptor_index)
+ if (outputDevice < 0 || outputDevice >= pm_descriptor_len)
err = pmInvalidDeviceId;
- else if (!descriptors[outputDevice].pub.output)
+ else if (!pm_descriptors[outputDevice].pub.output)
err = pmInvalidDeviceId;
- else if (descriptors[outputDevice].pub.opened)
+ else if (pm_descriptors[outputDevice].pub.opened)
err = pmInvalidDeviceId;
if (err != pmNoError)
goto error_return;
- /* create portMidi internal data */
- midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
- *stream = midi;
- if (!midi) {
- err = pmInsufficientMemory;
+ /* common initialization of PmInternal structure (midi): */
+ err = pm_create_internal(&midi, outputDevice, FALSE, latency, time_proc,
+ time_info, bufferSize);
+ *stream = midi;
+ if (err) {
goto error_return;
}
- midi->device_id = outputDevice;
- midi->write_flag = TRUE;
- midi->time_proc = time_proc;
- /* if latency > 0, we need a time reference. If none is provided,
- use PortTime library */
- if (time_proc == NULL && latency != 0) {
- if (!Pt_Started())
- Pt_Start(1, 0, 0);
- /* time_get does not take a parameter, so coerce */
- midi->time_proc = (PmTimeProcPtr) Pt_Time;
- }
- midi->time_info = time_info;
- midi->buffer_len = bufferSize;
- midi->queue = NULL; /* unused by output */
- /* if latency zero, output immediate (timestamps ignored) */
- /* if latency < 0, use 0 but don't return an error */
- if (latency < 0) latency = 0;
- midi->latency = latency;
- midi->sysex_in_progress = FALSE;
- midi->sysex_message = 0; /* unused by output */
- midi->sysex_message_count = 0; /* unused by output */
- midi->filters = 0; /* not used for output */
- midi->channel_mask = 0xFFFF;
- midi->sync_time = 0;
- midi->first_message = TRUE;
- midi->dictionary = descriptors[outputDevice].dictionary;
- midi->fill_base = NULL;
- midi->fill_offset_ptr = NULL;
- midi->fill_length = 0;
- descriptors[outputDevice].internalDescriptor = midi;
+
/* open system dependent output device */
err = (*midi->dictionary->open)(midi, outputDriverInfo);
if (err) {
*stream = NULL;
- descriptors[outputDevice].internalDescriptor = NULL;
+ pm_descriptors[outputDevice].pm_internal = NULL;
/* free portMidi data */
pm_free(midi);
} else {
/* portMidi input open successful */
- descriptors[outputDevice].pub.opened = TRUE;
+ pm_descriptors[outputDevice].pub.opened = TRUE;
}
error_return:
/* note: system-dependent code must set pm_hosterror and
@@ -828,6 +1001,91 @@ error_return:
}
+static PmError create_virtual_device(const char *name, const char *interf,
+ void *device_info, int is_input)
+{
+ PmError err = pmNoError;
+ int i;
+ pm_hosterror = FALSE;
+
+ /* arg checking */
+ if (!name) {
+ err = pmInvalidDeviceId;
+ goto error_return;
+ }
+
+ Pm_Initialize(); /* just in case */
+
+ /* create the virtual device */
+ if (pm_interf_list_len == 0) {
+ return pmNotImplemented;
+ }
+ if (!interf) {
+ /* default interface is the first one */
+ interf = pm_interf_list[0].interf;
+ }
+ /* look up and call the create_fn for interf(ace), e.g. "CoreMIDI" */
+ for (i = 0; i < pm_interf_list_len; i++) {
+ if (strcmp(pm_interf_list[i].interf, interf) == 0) {
+ int id = (*pm_interf_list[i].create_fn)(is_input,
+ name, device_info);
+ /* id could be pmNameConflict or an actual descriptor index */
+ if (id >= 0) {
+ pm_descriptors[id].pub.is_virtual = TRUE;
+ }
+ err = id;
+ goto error_return;
+ }
+ }
+ err = pmInterfaceNotSupported;
+
+error_return:
+ /* note: if there is a pmHostError, it is the responsibility
+ * of the system-dependent code (*midi->dictionary->open)()
+ * to set pm_hosterror and pm_hosterror_text
+ */
+ return pm_errmsg(err);
+}
+
+
+PMEXPORT PmError Pm_CreateVirtualInput(const char *name,
+ const char *interf,
+ void *deviceInfo)
+{
+ return create_virtual_device(name, interf, deviceInfo, TRUE);
+}
+
+PMEXPORT PmError Pm_CreateVirtualOutput(const char *name, const char *interf,
+ void *deviceInfo)
+{
+ return create_virtual_device(name, interf, deviceInfo, FALSE);
+}
+
+PmError Pm_DeleteVirtualDevice(PmDeviceID id)
+{
+ int i;
+ const char *interf = pm_descriptors[id].pub.interf;
+ PmError err = pmBadData; /* returned if we cannot find the interface-
+ * specific delete function */
+ /* arg checking */
+ if (id < 0 || id >= pm_descriptor_len ||
+ pm_descriptors[id].pub.opened || pm_descriptors[id].deleted) {
+ return pmInvalidDeviceId;
+ }
+ /* delete function pointer is in interfaces list */
+ for (i = 0; i < pm_interf_list_len; i++) {
+ if (strcmp(pm_interf_list[i].interf, interf) == 0) {
+ err = (*pm_interf_list[i].delete_fn)(id);
+ break;
+ }
+ }
+ pm_descriptors[id].deleted = TRUE;
+ /* (pm_internal should already be NULL because !pub.opened) */
+ pm_descriptors[id].pm_internal = NULL;
+ pm_descriptors[id].descriptor = NULL;
+ return err;
+}
+
PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
{
PmInternal *midi = (PmInternal *) stream;
@@ -842,14 +1100,15 @@ PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
}
-PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) {
+PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
/* arg checking */
if (midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
else
midi->filters = filters;
@@ -857,7 +1116,8 @@ PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) {
}
-PMEXPORT PmError Pm_Close( PortMidiStream *stream ) {
+PMEXPORT PmError Pm_Close(PortMidiStream *stream)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
@@ -866,10 +1126,10 @@ PMEXPORT PmError Pm_Close( PortMidiStream *stream ) {
if (midi == NULL) /* midi must point to something */
err = pmBadPtr;
/* if it is an open device, the device_id will be valid */
- else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_index)
+ else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_len)
err = pmBadPtr;
/* and the device should be in the opened state */
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
if (err != pmNoError)
@@ -878,8 +1138,8 @@ PMEXPORT PmError Pm_Close( PortMidiStream *stream ) {
/* close the device */
err = (*midi->dictionary->close)(midi);
/* even if an error occurred, continue with cleanup */
- descriptors[midi->device_id].internalDescriptor = NULL;
- descriptors[midi->device_id].pub.opened = FALSE;
+ pm_descriptors[midi->device_id].pm_internal = NULL;
+ pm_descriptors[midi->device_id].pub.opened = FALSE;
if (midi->queue) Pm_QueueDestroy(midi->queue);
pm_free(midi);
error_return:
@@ -889,56 +1149,57 @@ error_return:
return pm_errmsg(err);
}
-PmError Pm_Synchronize( PortMidiStream* stream ) {
+PmError Pm_Synchronize(PortMidiStream* stream)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
if (midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.output)
+ else if (!pm_descriptors[midi->device_id].pub.output)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
else
midi->first_message = TRUE;
return err;
}
-PMEXPORT PmError Pm_Abort( PortMidiStream* stream ) {
+PMEXPORT PmError Pm_Abort(PortMidiStream* stream)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err;
/* arg checking */
if (midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.output)
+ else if (!pm_descriptors[midi->device_id].pub.output)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
else
err = (*midi->dictionary->abort)(midi);
if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
+ midi->dictionary->check_host_error(midi);
}
return pm_errmsg(err);
}
-/* pm_channel_filtered returns non-zero if the channel mask is blocking the current channel */
+/* pm_channel_filtered returns non-zero if the channel mask is
+ blocking the current channel */
#define pm_channel_filtered(status, mask) \
((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask))))
-/* The following two functions will checks to see if a MIDI message matches
- the filtering criteria. Since the sysex routines only want to filter realtime messages,
- we need to have separate routines.
+/* The following two functions will checks to see if a MIDI message
+ matches the filtering criteria. Since the sysex routines only want
+ to filter realtime messages, we need to have separate routines.
*/
-/* pm_realtime_filtered returns non-zero if the filter will kill the current message.
- Note that only realtime messages are checked here.
+/* pm_realtime_filtered returns non-zero if the filter will kill the
+ current message. Note that only realtime messages are checked here.
*/
#define pm_realtime_filtered(status, filters) \
((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters)))
@@ -959,18 +1220,21 @@ PMEXPORT PmError Pm_Abort( PortMidiStream* stream ) {
}*/
-/* pm_status_filtered returns non-zero if a filter will kill the current message, based on status.
- Note that sysex and real time are not checked. It is up to the subsystem (winmm, core midi, alsa)
- to filter sysex, as it is handled more easily and efficiently at that level.
- Realtime message are filtered in pm_realtime_filtered.
+/* pm_status_filtered returns non-zero if a filter will kill the
+ current message, based on status. Note that sysex and real time are
+ not checked. It is up to the subsystem (winmm, core midi, alsa) to
+ filter sysex, as it is handled more easily and efficiently at that
+ level. Realtime message are filtered in pm_realtime_filtered.
*/
-#define pm_status_filtered(status, filters) ((1 << (16 + ((status) >> 4))) & (filters))
+#define pm_status_filtered(status, filters) \
+ ((1 << (16 + ((status) >> 4))) & (filters))
/*
return ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE))
|| ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE))
- || ((status == MIDI_CHANNEL_AT) && (filters & PM_FILT_CHANNEL_AFTERTOUCH))
+ || ((status == MIDI_CHANNEL_AT) &&
+ (filters & PM_FILT_CHANNEL_AFTERTOUCH))
|| ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH))
|| ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM))
|| ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL))