diff options
Diffstat (limited to '3rdparty/portmidi/pm_common/portmidi.c')
-rw-r--r-- | 3rdparty/portmidi/pm_common/portmidi.c | 720 |
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)) |