mirror of
				https://github.com/cookiengineer/audacity
				synced 2025-11-03 23:53:55 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1125 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1125 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#ifdef _MSC_VER
 | 
						|
 #pragma warning(disable: 4244) // stop warnings about downsize typecasts
 | 
						|
 #pragma warning(disable: 4018) // stop warnings about signed/unsigned
 | 
						|
#endif
 | 
						|
 | 
						|
#include "stdlib.h"
 | 
						|
#include "string.h"
 | 
						|
#include "portmidi.h"
 | 
						|
#include "porttime.h"
 | 
						|
#include "pmutil.h"
 | 
						|
#include "pminternal.h"
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
#define MIDI_CLOCK      0xf8
 | 
						|
#define MIDI_ACTIVE     0xfe
 | 
						|
#define MIDI_STATUS_MASK 0x80
 | 
						|
#define MIDI_SYSEX      0xf0
 | 
						|
#define MIDI_EOX        0xf7
 | 
						|
#define MIDI_START      0xFA
 | 
						|
#define MIDI_STOP       0xFC
 | 
						|
#define MIDI_CONTINUE   0xFB
 | 
						|
#define MIDI_F9         0xF9
 | 
						|
#define MIDI_FD         0xFD
 | 
						|
#define MIDI_RESET      0xFF
 | 
						|
#define MIDI_NOTE_ON    0x90
 | 
						|
#define MIDI_NOTE_OFF   0x80
 | 
						|
#define MIDI_CHANNEL_AT 0xD0
 | 
						|
#define MIDI_POLY_AT    0xA0
 | 
						|
#define MIDI_PROGRAM    0xC0
 | 
						|
#define MIDI_CONTROL    0xB0
 | 
						|
#define MIDI_PITCHBEND  0xE0
 | 
						|
#define MIDI_MTC        0xF1
 | 
						|
#define MIDI_SONGPOS    0xF2
 | 
						|
#define MIDI_SONGSEL    0xF3
 | 
						|
#define MIDI_TUNE       0xF6
 | 
						|
 | 
						|
#define is_empty(midi) ((midi)->tail == (midi)->head)
 | 
						|
 | 
						|
/* this is not static so that pm_init can set it directly if
 | 
						|
 *   (see pmmac.c:pm_init())
 | 
						|
 */
 | 
						|
int pm_initialized = FALSE;
 | 
						|
 | 
						|
int pm_hosterror;
 | 
						|
char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
 | 
						|
 | 
						|
#ifdef PM_CHECK_ERRORS
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#define STRING_MAX 80
 | 
						|
 | 
						|
static void prompt_and_exit(void)
 | 
						|
{
 | 
						|
    char line[STRING_MAX];
 | 
						|
    printf("type ENTER...");
 | 
						|
    fgets(line, STRING_MAX, stdin);
 | 
						|
    /* this will clean up open ports: */
 | 
						|
    exit(-1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static PmError pm_errmsg(PmError err)
 | 
						|
{
 | 
						|
    if (err == pmHostError) {
 | 
						|
        /* it seems pointless to allocate memory and copy the string,
 | 
						|
         * so I will do the work of Pm_GetHostErrorText directly
 | 
						|
         */
 | 
						|
        printf("PortMidi found host error...\n  %s\n", pm_hosterror_text);
 | 
						|
        pm_hosterror = FALSE;
 | 
						|
        pm_hosterror_text[0] = 0; /* clear the message */
 | 
						|
        prompt_and_exit();
 | 
						|
    } else if (err < 0) {
 | 
						|
        printf("PortMidi call failed...\n  %s\n", Pm_GetErrorText(err));
 | 
						|
        prompt_and_exit();
 | 
						|
    }
 | 
						|
    return err;
 | 
						|
}
 | 
						|
#else
 | 
						|
#define pm_errmsg(err) err
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
====================================================================
 | 
						|
system implementation of portmidi interface
 | 
						|
====================================================================
 | 
						|
*/
 | 
						|
 | 
						|
int pm_descriptor_max = 0;
 | 
						|
int pm_descriptor_index = 0;
 | 
						|
descriptor_type descriptors = NULL;
 | 
						|
 | 
						|
/* 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!
 | 
						|
 *
 | 
						|
 * returns pmInvalidDeviceId if device memory is exceeded
 | 
						|
 * otherwise returns pmNoError
 | 
						|
 */
 | 
						|
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
 | 
						|
        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, 
 | 
						|
                   sizeof(descriptor_node) * pm_descriptor_max);
 | 
						|
            free(descriptors);
 | 
						|
        }
 | 
						|
        pm_descriptor_max += 32;
 | 
						|
        descriptors = new_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;
 | 
						|
 | 
						|
    /* default state: nothing to close (for automatic device closing) */
 | 
						|
    descriptors[pm_descriptor_index].pub.opened = FALSE;
 | 
						|
 | 
						|
    /* ID number passed to win32 multimedia API open */
 | 
						|
    descriptors[pm_descriptor_index].descriptor = descriptor;
 | 
						|
    
 | 
						|
    /* points to PmInternal, allows automatic device closing */
 | 
						|
    descriptors[pm_descriptor_index].internalDescriptor = NULL;
 | 
						|
 | 
						|
    descriptors[pm_descriptor_index].dictionary = dictionary;
 | 
						|
    
 | 
						|
    pm_descriptor_index++;
 | 
						|
    
 | 
						|
    return pmNoError;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* utility to look up device, given a pattern, 
 | 
						|
   note: pattern is modified
 | 
						|
 */
 | 
						|
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 = ""; /* initially assume it is not there */
 | 
						|
    char *name_pref = strstr(pattern, ", ");
 | 
						|
 | 
						|
    if (name_pref) { /* found separator, adjust the pointer */
 | 
						|
        interf_pref = pattern;
 | 
						|
        name_pref[0] = 0;
 | 
						|
        name_pref += 2;
 | 
						|
    } else {
 | 
						|
        name_pref = pattern; /* whole string is the name pattern */
 | 
						|
    }
 | 
						|
    for (i = 0; i < pm_descriptor_index; i++) {
 | 
						|
        const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
 | 
						|
        if (info->input == is_input &&
 | 
						|
            strstr(info->name, name_pref) &&
 | 
						|
            strstr(info->interf, interf_pref)) {
 | 
						|
            id = i;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }    
 | 
						|
    return id;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
====================================================================
 | 
						|
portmidi implementation
 | 
						|
====================================================================
 | 
						|
*/
 | 
						|
 | 
						|
int Pm_CountDevices( void ) {
 | 
						|
    Pm_Initialize();
 | 
						|
    /* no error checking -- Pm_Initialize() does not fail */
 | 
						|
    return pm_descriptor_index;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) {
 | 
						|
    Pm_Initialize(); /* no error check needed */
 | 
						|
    if (id >= 0 && id < pm_descriptor_index) {
 | 
						|
        return &descriptors[id].pub;
 | 
						|
    }
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* pm_success_fn -- "noop" function pointer */
 | 
						|
PmError pm_success_fn(PmInternal *midi) {
 | 
						|
    return pmNoError;
 | 
						|
}
 | 
						|
 | 
						|
/* none_write -- returns an error if called */
 | 
						|
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) {
 | 
						|
    return pmBadPtr;
 | 
						|
}
 | 
						|
 | 
						|
PmError none_write_byte(PmInternal *midi, unsigned char byte, 
 | 
						|
                        PmTimestamp timestamp) {
 | 
						|
    return pmBadPtr;
 | 
						|
}
 | 
						|
 | 
						|
/* pm_fail_fn -- generic function, returns error if called */
 | 
						|
PmError pm_fail_fn(PmInternal *midi) {
 | 
						|
    return pmBadPtr;
 | 
						|
}
 | 
						|
 | 
						|
static PmError none_open(PmInternal *midi, void *driverInfo) {
 | 
						|
    return pmBadPtr;
 | 
						|
}
 | 
						|
static void none_get_host_error(PmInternal * midi, char * msg, unsigned int len) {
 | 
						|
    strcpy(msg, "");
 | 
						|
}
 | 
						|
static unsigned int none_has_host_error(PmInternal * midi) {
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
PmTimestamp none_synchronize(PmInternal *midi) {
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#define none_abort pm_fail_fn
 | 
						|
#define none_close pm_fail_fn
 | 
						|
 | 
						|
pm_fns_node pm_none_dictionary = {
 | 
						|
    none_write_short,
 | 
						|
    none_sysex,
 | 
						|
    none_sysex,
 | 
						|
    none_write_byte,
 | 
						|
    none_write_short,
 | 
						|
    none_write_flush,
 | 
						|
    none_synchronize,
 | 
						|
    none_open,
 | 
						|
    none_abort, 
 | 
						|
    none_close,
 | 
						|
    none_poll,
 | 
						|
    none_has_host_error,
 | 
						|
    none_get_host_error 
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
const char *Pm_GetErrorText( PmError errnum ) {
 | 
						|
    const char *msg;
 | 
						|
 | 
						|
    switch(errnum)
 | 
						|
    {
 | 
						|
    case pmNoError:                  
 | 
						|
        msg = ""; 
 | 
						|
        break;
 | 
						|
    case pmHostError:                
 | 
						|
        msg = "PortMidi: `Host error'"; 
 | 
						|
        break;
 | 
						|
    case pmInvalidDeviceId:          
 | 
						|
        msg = "PortMidi: `Invalid device ID'"; 
 | 
						|
        break;
 | 
						|
    case pmInsufficientMemory:       
 | 
						|
        msg = "PortMidi: `Insufficient memory'"; 
 | 
						|
        break;
 | 
						|
    case pmBufferTooSmall:           
 | 
						|
        msg = "PortMidi: `Buffer too small'"; 
 | 
						|
        break;
 | 
						|
    case pmBadPtr:                   
 | 
						|
        msg = "PortMidi: `Bad pointer'"; 
 | 
						|
        break;
 | 
						|
    case pmInternalError:            
 | 
						|
        msg = "PortMidi: `Internal PortMidi Error'"; 
 | 
						|
        break;
 | 
						|
    case pmBufferOverflow:
 | 
						|
        msg = "PortMidi: `Buffer overflow'";
 | 
						|
        break;
 | 
						|
    case pmBadData:
 | 
						|
        msg = "PortMidi: `Invalid MIDI message Data'";
 | 
						|
        break;
 | 
						|
    case pmBufferMaxSize:
 | 
						|
        msg = "PortMidi: `Buffer cannot be made larger'";
 | 
						|
        break;
 | 
						|
    default:                         
 | 
						|
        msg = "PortMidi: `Illegal error number'"; 
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return msg;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This can be called whenever you get a pmHostError return value.
 | 
						|
 * The error will always be in the global pm_hosterror_text.
 | 
						|
 */
 | 
						|
void Pm_GetHostErrorText(char * msg, unsigned int len) {
 | 
						|
    assert(msg);
 | 
						|
    assert(len > 0);
 | 
						|
    if (pm_hosterror) {
 | 
						|
        strncpy(msg, (char *) pm_hosterror_text, len);
 | 
						|
        pm_hosterror = FALSE;
 | 
						|
        pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it
 | 
						|
                                     might help with debugging */
 | 
						|
        msg[len - 1] = 0; /* make sure string is terminated */
 | 
						|
    } else {
 | 
						|
        msg[0] = 0; /* no string to return */
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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 FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
PmError Pm_Initialize( void ) {
 | 
						|
    if (!pm_initialized) {
 | 
						|
        pm_hosterror = FALSE;
 | 
						|
        pm_hosterror_text[0] = 0; /* the null string */
 | 
						|
        pm_init();
 | 
						|
        pm_initialized = TRUE;
 | 
						|
    }
 | 
						|
    return pmNoError;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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;
 | 
						|
        }
 | 
						|
        pm_descriptor_index = 0;
 | 
						|
        pm_descriptor_max = 0;
 | 
						|
        pm_initialized = FALSE;
 | 
						|
    }
 | 
						|
    return pmNoError;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Pm_Read -- read up to length longs from source into buffer */
 | 
						|
/*
 | 
						|
 * returns number of longs actually read, or error code
 | 
						|
 */
 | 
						|
int Pm_Read(PortMidiStream *stream, PmEvent *buffer, long length) {
 | 
						|
    PmInternal *midi = (PmInternal *) stream;
 | 
						|
    int n = 0;
 | 
						|
    PmError err = pmNoError;
 | 
						|
    pm_hosterror = FALSE;
 | 
						|
    /* arg checking */
 | 
						|
    if(midi == NULL)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if(!descriptors[midi->device_id].pub.opened)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if(!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
 | 
						|
     * with data from the MIDI hardware; this depends on the implementation.
 | 
						|
     * We could call Pm_Poll here, but that would redo a lot of redundant
 | 
						|
     * parameter checking, so I copied some code from Pm_Poll to here: */
 | 
						|
    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);
 | 
						|
    }
 | 
						|
 | 
						|
    while (n < length) {
 | 
						|
        PmError err = Pm_Dequeue(midi->queue, buffer++);
 | 
						|
        if (err == pmBufferOverflow) {
 | 
						|
            /* ignore the data we have retreived so far */
 | 
						|
            return pm_errmsg(pmBufferOverflow);
 | 
						|
        } else if (err == 0) { /* empty queue */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        n++;
 | 
						|
    }
 | 
						|
    return n;
 | 
						|
}
 | 
						|
 | 
						|
PmError Pm_Poll( PortMidiStream *stream )
 | 
						|
{
 | 
						|
    PmInternal *midi = (PmInternal *) stream;
 | 
						|
    PmError err;
 | 
						|
 | 
						|
    pm_hosterror = FALSE;
 | 
						|
    /* arg checking */
 | 
						|
    if(midi == NULL)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if (!descriptors[midi->device_id].pub.opened)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if (!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);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* this is called from Pm_Write and Pm_WriteSysEx to issue a
 | 
						|
 * call to the system-dependent end_sysex function and handle 
 | 
						|
 * the error return
 | 
						|
 */
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* to facilitate correct error-handling, Pm_Write, Pm_WriteShort, and
 | 
						|
   Pm_WriteSysEx all operate a state machine that "outputs" calls to
 | 
						|
   write_short, begin_sysex, write_byte, end_sysex, and write_realtime */
 | 
						|
 | 
						|
PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length)
 | 
						|
{
 | 
						|
    PmInternal *midi = (PmInternal *) stream;
 | 
						|
    PmError err = pmNoError;
 | 
						|
    int i;
 | 
						|
    int bits;
 | 
						|
    
 | 
						|
    pm_hosterror = FALSE;
 | 
						|
    /* arg checking */
 | 
						|
    if(midi == NULL)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if(!descriptors[midi->device_id].pub.opened)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if(!descriptors[midi->device_id].pub.output)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else
 | 
						|
        err = pmNoError;
 | 
						|
    
 | 
						|
    if (err != pmNoError) goto pm_write_error;
 | 
						|
    
 | 
						|
    if (midi->latency == 0) {
 | 
						|
        midi->now = 0;
 | 
						|
    } else {
 | 
						|
        midi->now = (*(midi->time_proc))(midi->time_info);
 | 
						|
        if (midi->first_message || midi->sync_time + 100 /*ms*/ < midi->now) {
 | 
						|
            /* time to resync */
 | 
						|
            midi->now = (*midi->dictionary->synchronize)(midi);
 | 
						|
            midi->first_message = FALSE;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /* error recovery: when a sysex is detected, we call
 | 
						|
     *   dictionary->begin_sysex() followed by calls to
 | 
						|
     *   dictionary->write_byte() and dictionary->write_realtime()
 | 
						|
     *   until an end-of-sysex is detected, when we call
 | 
						|
     *   dictionary->end_sysex(). After an error occurs, 
 | 
						|
     *   Pm_Write() continues to call functions. For example,
 | 
						|
     *   it will continue to call write_byte() even after
 | 
						|
     *   an error sending a sysex message, and end_sysex() will be
 | 
						|
     *   called when an EOX or non-real-time status is found.
 | 
						|
     * When errors are detected, Pm_Write() returns immediately, 
 | 
						|
     *   so it is possible that this will drop data and leave
 | 
						|
     *   sysex messages in a partially transmitted state.
 | 
						|
     */
 | 
						|
    for (i = 0; i < length; i++) {
 | 
						|
        unsigned long msg = buffer[i].message;
 | 
						|
        bits = 0;
 | 
						|
        /* is this a sysex message? */
 | 
						|
        if (Pm_MessageStatus(msg) == MIDI_SYSEX) {
 | 
						|
            if (midi->sysex_in_progress) {
 | 
						|
                /* error: previous sysex was not terminated by EOX */
 | 
						|
                midi->sysex_in_progress = FALSE;
 | 
						|
                err = pmBadData;
 | 
						|
                goto pm_write_error;
 | 
						|
            }
 | 
						|
            midi->sysex_in_progress = TRUE;
 | 
						|
            if ((err = (*midi->dictionary->begin_sysex)(midi, 
 | 
						|
                               buffer[i].timestamp)) != pmNoError)
 | 
						|
                goto pm_write_error;
 | 
						|
            if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX,
 | 
						|
                               buffer[i].timestamp)) != pmNoError) 
 | 
						|
                goto pm_write_error;
 | 
						|
            bits = 8;
 | 
						|
            /* fall through to continue sysex processing */
 | 
						|
        } else if ((msg & MIDI_STATUS_MASK) && 
 | 
						|
                   (Pm_MessageStatus(msg) != MIDI_EOX)) {
 | 
						|
            /* a non-sysex message */
 | 
						|
            if (midi->sysex_in_progress) {
 | 
						|
                /* this should be a realtime message */
 | 
						|
                if (is_real_time(msg)) {
 | 
						|
                    if ((err = (*midi->dictionary->write_realtime)(midi, 
 | 
						|
                                       &(buffer[i]))) != pmNoError)
 | 
						|
                        goto pm_write_error;
 | 
						|
                } else {
 | 
						|
                    midi->sysex_in_progress = FALSE;
 | 
						|
                    err = pmBadData;
 | 
						|
                    /* ignore any error from this, because we already have one */
 | 
						|
                    /* pass 0 as timestamp -- it's ignored */
 | 
						|
                    (*midi->dictionary->end_sysex)(midi, 0);
 | 
						|
                    goto pm_write_error;
 | 
						|
                }
 | 
						|
            } else { /* regular short midi message */
 | 
						|
                if ((err = (*midi->dictionary->write_short)(midi, 
 | 
						|
                                   &(buffer[i]))) != pmNoError)
 | 
						|
                    goto pm_write_error;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (midi->sysex_in_progress) { /* send sysex bytes until EOX */
 | 
						|
            /* see if we can accelerate data transfer */
 | 
						|
            if (bits == 0 && midi->fill_base && /* 4 bytes to copy */
 | 
						|
                (*midi->fill_offset_ptr) + 4 <= midi->fill_length &&
 | 
						|
                (msg & 0x80808080) == 0) { /* all data */
 | 
						|
                    /* copy 4 bytes from msg to fill_base + fill_offset */
 | 
						|
                    unsigned char *ptr = midi->fill_base + 
 | 
						|
                                         *(midi->fill_offset_ptr);
 | 
						|
                    ptr[0] = msg; ptr[1] = msg >> 8; 
 | 
						|
                    ptr[2] = msg >> 18; ptr[3] = msg >> 24;
 | 
						|
                    (*midi->fill_offset_ptr) += 4;
 | 
						|
                     continue;
 | 
						|
            }
 | 
						|
            /* no acceleration, so do byte-by-byte copying */
 | 
						|
            while (bits < 32) {
 | 
						|
                unsigned char midi_byte = (unsigned char) (msg >> bits);
 | 
						|
                if ((err = (*midi->dictionary->write_byte)(midi, midi_byte, 
 | 
						|
                                   buffer[i].timestamp)) != pmNoError)
 | 
						|
                    goto pm_write_error;
 | 
						|
                if (midi_byte == MIDI_EOX) {
 | 
						|
                    err = pm_end_sysex(midi);
 | 
						|
                    if (err != pmNoError) goto error_exit;
 | 
						|
                    break; /* from while loop */
 | 
						|
                }
 | 
						|
                bits += 8;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            /* not in sysex mode, but message did not start with status */
 | 
						|
            err = pmBadData;
 | 
						|
            goto pm_write_error;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /* after all messages are processed, send the data */
 | 
						|
    if (!midi->sysex_in_progress)
 | 
						|
        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;
 | 
						|
    }
 | 
						|
error_exit:
 | 
						|
    return pm_errmsg(err);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
PmError Pm_WriteShort(PortMidiStream *stream, long when, long msg)
 | 
						|
{
 | 
						|
    PmEvent event;
 | 
						|
    
 | 
						|
    event.timestamp = when;
 | 
						|
    event.message = msg;
 | 
						|
    return Pm_Write(stream, &event, 1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, 
 | 
						|
                      unsigned char *msg)
 | 
						|
{
 | 
						|
    /* allocate buffer space for PM_DEFAULT_SYSEX_BUFFER_SIZE bytes */
 | 
						|
    /* each PmEvent holds sizeof(PmMessage) bytes of sysex data */
 | 
						|
    #define BUFLEN (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage))
 | 
						|
    PmEvent buffer[BUFLEN];
 | 
						|
    int buffer_size = 1; /* first time, send 1. After that, it's BUFLEN */
 | 
						|
    PmInternal *midi = (PmInternal *) stream;
 | 
						|
    /* the next byte in the buffer is represented by an index, bufx, and
 | 
						|
       a shift in bits */
 | 
						|
    int shift = 0;
 | 
						|
    int bufx = 0;
 | 
						|
    buffer[0].message = 0;
 | 
						|
    buffer[0].timestamp = when;
 | 
						|
 | 
						|
    while (1) {
 | 
						|
        /* insert next byte into buffer */
 | 
						|
        buffer[bufx].message |= ((*msg) << shift);
 | 
						|
        shift += 8;
 | 
						|
        if (*msg++ == MIDI_EOX) break;
 | 
						|
        if (shift == 32) {
 | 
						|
            shift = 0;
 | 
						|
            bufx++;
 | 
						|
            if (bufx == buffer_size) {
 | 
						|
                PmError err = Pm_Write(stream, buffer, buffer_size);
 | 
						|
                /* note: Pm_Write has already called errmsg() */
 | 
						|
                if (err) return err;
 | 
						|
                /* prepare to fill another buffer */
 | 
						|
                bufx = 0;
 | 
						|
                buffer_size = BUFLEN;
 | 
						|
                /* optimization: maybe we can just copy bytes */
 | 
						|
                if (midi->fill_base) {
 | 
						|
                    PmError err;
 | 
						|
                    while (*(midi->fill_offset_ptr) < midi->fill_length) {
 | 
						|
                        midi->fill_base[(*midi->fill_offset_ptr)++] = *msg;
 | 
						|
                        if (*msg++ == MIDI_EOX) {
 | 
						|
                            err = pm_end_sysex(midi);
 | 
						|
                            if (err != pmNoError) return pm_errmsg(err);
 | 
						|
                            goto end_of_sysex;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    /* I thought that I could do a pm_Write here and
 | 
						|
                     * change this if to a loop, avoiding calls in Pm_Write
 | 
						|
                     * to the slower write_byte, but since 
 | 
						|
                     * sysex_in_progress is true, this will not flush
 | 
						|
                     * the buffer, and we'll infinite loop: */
 | 
						|
                    /* err = Pm_Write(stream, buffer, 0);
 | 
						|
                       if (err) return err; */
 | 
						|
                    /* instead, the way this works is that Pm_Write calls
 | 
						|
                     * write_byte on 4 bytes. The first, since the buffer
 | 
						|
                     * is full, will flush the buffer and allocate a new
 | 
						|
                     * one. This primes the buffer so
 | 
						|
                     * that we can return to the loop above and fill it
 | 
						|
                     * efficiently without a lot of function calls.
 | 
						|
                     */
 | 
						|
                    buffer_size = 1; /* get another message started */
 | 
						|
                }
 | 
						|
            }
 | 
						|
            buffer[bufx].message = 0;
 | 
						|
            buffer[bufx].timestamp = when;
 | 
						|
        } 
 | 
						|
        /* keep inserting bytes until you find MIDI_EOX */
 | 
						|
    }
 | 
						|
end_of_sysex:
 | 
						|
    /* we're finished sending full buffers, but there may
 | 
						|
     * be a partial one left.
 | 
						|
     */
 | 
						|
    if (shift != 0) bufx++; /* add partial message to buffer len */
 | 
						|
    if (bufx) { /* bufx is number of PmEvents to send from buffer */
 | 
						|
        PmError err = Pm_Write(stream, buffer, bufx);
 | 
						|
        if (err) return err;
 | 
						|
    }
 | 
						|
    return pmNoError;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
PmError Pm_OpenInput(PortMidiStream** stream,
 | 
						|
                     PmDeviceID inputDevice,
 | 
						|
                     void *inputDriverInfo,
 | 
						|
                     long bufferSize,
 | 
						|
                     PmTimeProcPtr time_proc,
 | 
						|
                     void *time_info)
 | 
						|
{
 | 
						|
    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;
 | 
						|
 | 
						|
    /* create portMidi internal data */
 | 
						|
    midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); 
 | 
						|
    *stream = midi;
 | 
						|
    if (!midi) {
 | 
						|
        err = pmInsufficientMemory;
 | 
						|
        goto error_return;
 | 
						|
    }
 | 
						|
    midi->device_id = inputDevice;
 | 
						|
    midi->write_flag = FALSE;
 | 
						|
    midi->time_proc = time_proc;
 | 
						|
    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, sizeof(PmEvent));
 | 
						|
    if (!midi->queue) {
 | 
						|
        /* free portMidi data */
 | 
						|
        *stream = NULL;
 | 
						|
        pm_free(midi); 
 | 
						|
        err = pmInsufficientMemory;
 | 
						|
        goto error_return;
 | 
						|
    }
 | 
						|
    midi->buffer_len = bufferSize; /* portMidi input storage */
 | 
						|
    midi->latency = 0; /* not used */
 | 
						|
    midi->sysex_in_progress = FALSE;
 | 
						|
    midi->sysex_message = 0; 
 | 
						|
    midi->sysex_message_count = 0; 
 | 
						|
    midi->filters = PM_FILT_ACTIVE;
 | 
						|
    midi->channel_mask = 0xFFFF;
 | 
						|
    midi->sync_time = 0;
 | 
						|
    midi->first_message = TRUE;
 | 
						|
    midi->dictionary = descriptors[inputDevice].dictionary;
 | 
						|
    midi->fill_base = NULL;
 | 
						|
    midi->fill_offset_ptr = NULL;
 | 
						|
    midi->fill_length = 0;
 | 
						|
    descriptors[inputDevice].internalDescriptor = midi;
 | 
						|
    /* open system dependent input device */
 | 
						|
    err = (*midi->dictionary->open)(midi, inputDriverInfo);
 | 
						|
    if (err) {
 | 
						|
        *stream = NULL;
 | 
						|
        descriptors[inputDevice].internalDescriptor = NULL;
 | 
						|
        /* free portMidi data */
 | 
						|
        Pm_QueueDestroy(midi->queue);
 | 
						|
        pm_free(midi);
 | 
						|
    } else {
 | 
						|
        /* portMidi input open successful */
 | 
						|
        descriptors[inputDevice].pub.opened = TRUE;
 | 
						|
    }
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
PmError Pm_OpenOutput(PortMidiStream** stream,
 | 
						|
                      PmDeviceID outputDevice,
 | 
						|
                      void *outputDriverInfo,
 | 
						|
                      long bufferSize,
 | 
						|
                      PmTimeProcPtr time_proc,
 | 
						|
                      void *time_info,
 | 
						|
                      long latency ) 
 | 
						|
{
 | 
						|
    PmInternal *midi;
 | 
						|
    PmError err = pmNoError;
 | 
						|
    pm_hosterror = FALSE;
 | 
						|
    *stream =  NULL;
 | 
						|
    
 | 
						|
    /* arg checking */
 | 
						|
    if (outputDevice < 0 || outputDevice >= pm_descriptor_index)
 | 
						|
        err = pmInvalidDeviceId;
 | 
						|
    else if (!descriptors[outputDevice].pub.output) 
 | 
						|
        err = pmInvalidDeviceId;
 | 
						|
    else if (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;
 | 
						|
        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;
 | 
						|
        /* free portMidi data */
 | 
						|
        pm_free(midi); 
 | 
						|
    } else {
 | 
						|
        /* portMidi input open successful */
 | 
						|
        descriptors[outputDevice].pub.opened = TRUE;
 | 
						|
    }
 | 
						|
error_return:
 | 
						|
    /* note: system-dependent code must set pm_hosterror and
 | 
						|
     * pm_hosterror_text if a pmHostError occurs
 | 
						|
     */
 | 
						|
    return pm_errmsg(err);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
 | 
						|
{
 | 
						|
    PmInternal *midi = (PmInternal *) stream;
 | 
						|
    PmError err = pmNoError;
 | 
						|
 | 
						|
    if (midi == NULL)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else
 | 
						|
        midi->channel_mask = mask;
 | 
						|
 | 
						|
    return pm_errmsg(err);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
PmError Pm_SetFilter(PortMidiStream *stream, long filters) {
 | 
						|
    PmInternal *midi = (PmInternal *) stream;
 | 
						|
    PmError err = pmNoError;
 | 
						|
 | 
						|
    /* arg checking */
 | 
						|
    if (midi == NULL)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if (!descriptors[midi->device_id].pub.opened)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else
 | 
						|
        midi->filters = filters;
 | 
						|
    return pm_errmsg(err);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
PmError Pm_Close( PortMidiStream *stream ) {
 | 
						|
    PmInternal *midi = (PmInternal *) stream;
 | 
						|
    PmError err = pmNoError;
 | 
						|
 | 
						|
    pm_hosterror = FALSE;
 | 
						|
    /* arg checking */
 | 
						|
    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)
 | 
						|
        err = pmBadPtr;
 | 
						|
    /* and the device should be in the opened state */
 | 
						|
    else if (!descriptors[midi->device_id].pub.opened)
 | 
						|
        err = pmBadPtr;
 | 
						|
    
 | 
						|
    if (err != pmNoError) 
 | 
						|
        goto error_return;
 | 
						|
 | 
						|
    /* 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;
 | 
						|
    if (midi->queue) Pm_QueueDestroy(midi->queue);
 | 
						|
    pm_free(midi); 
 | 
						|
error_return:
 | 
						|
    /* system dependent code must set pm_hosterror and
 | 
						|
     * pm_hosterror_text if a pmHostError occurs.
 | 
						|
     */
 | 
						|
    return pm_errmsg(err);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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)
 | 
						|
        err = pmBadPtr;
 | 
						|
    else if (!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;
 | 
						|
    }
 | 
						|
    return pm_errmsg(err);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* 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.
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
/* 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)))
 | 
						|
 | 
						|
/*
 | 
						|
    return ((status == MIDI_ACTIVE) && (filters & PM_FILT_ACTIVE))
 | 
						|
            ||  ((status == MIDI_CLOCK) && (filters & PM_FILT_CLOCK))
 | 
						|
            ||  ((status == MIDI_START) && (filters & PM_FILT_PLAY))
 | 
						|
            ||  ((status == MIDI_STOP) && (filters & PM_FILT_PLAY))
 | 
						|
            ||  ((status == MIDI_CONTINUE) && (filters & PM_FILT_PLAY))
 | 
						|
            ||  ((status == MIDI_F9) && (filters & PM_FILT_F9))
 | 
						|
            ||  ((status == MIDI_FD) && (filters & PM_FILT_FD))
 | 
						|
            ||  ((status == MIDI_RESET) && (filters & PM_FILT_RESET))
 | 
						|
            ||  ((status == MIDI_MTC) && (filters & PM_FILT_MTC))
 | 
						|
            ||  ((status == MIDI_SONGPOS) && (filters & PM_FILT_SONG_POSITION))
 | 
						|
            ||  ((status == MIDI_SONGSEL) && (filters & PM_FILT_SONG_SELECT))
 | 
						|
            ||  ((status == MIDI_TUNE) && (filters & PM_FILT_TUNE));
 | 
						|
}*/
 | 
						|
 | 
						|
 | 
						|
/* 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))
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
    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_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH))
 | 
						|
            ||  ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM))
 | 
						|
            ||  ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL))
 | 
						|
            ||  ((status == MIDI_PITCHBEND) && (filters & PM_FILT_PITCHBEND));
 | 
						|
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
static void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp)
 | 
						|
{
 | 
						|
    PmEvent event;
 | 
						|
    
 | 
						|
    /* there may be nothing in the buffer */
 | 
						|
    if (midi->sysex_message_count == 0) return; /* nothing to flush */
 | 
						|
    
 | 
						|
    event.message = midi->sysex_message;
 | 
						|
    event.timestamp = timestamp;
 | 
						|
    /* copied from pm_read_short, avoids filtering */
 | 
						|
    if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) {
 | 
						|
        midi->sysex_in_progress = FALSE;
 | 
						|
    }
 | 
						|
    midi->sysex_message_count = 0;
 | 
						|
    midi->sysex_message = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* pm_read_short and pm_read_bytes
 | 
						|
   are the interface between system-dependent MIDI input handlers
 | 
						|
   and the system-independent PortMIDI code.
 | 
						|
   The input handler MUST obey these rules:
 | 
						|
   1) all short input messages must be sent to pm_read_short, which
 | 
						|
      enqueues them to a FIFO for the application.
 | 
						|
   2) each buffer of sysex bytes should be reported by calling pm_read_bytes
 | 
						|
      (which sets midi->sysex_in_progress). After the eox byte, 
 | 
						|
      pm_read_bytes will clear sysex_in_progress
 | 
						|
 */
 | 
						|
 | 
						|
/* pm_read_short is the place where all input messages arrive from 
 | 
						|
   system-dependent code such as pmwinmm.c. Here, the messages
 | 
						|
   are entered into the PortMidi input buffer. 
 | 
						|
 */
 | 
						|
void pm_read_short(PmInternal *midi, PmEvent *event)
 | 
						|
{ 
 | 
						|
    int status;
 | 
						|
    /* arg checking */
 | 
						|
    assert(midi != NULL);
 | 
						|
    /* midi filtering is applied here */
 | 
						|
    status = Pm_MessageStatus(event->message);
 | 
						|
    if (!pm_status_filtered(status, midi->filters)
 | 
						|
        && (!is_real_time(status) || 
 | 
						|
            !pm_realtime_filtered(status, midi->filters))
 | 
						|
        && !pm_channel_filtered(status, midi->channel_mask)) {
 | 
						|
        /* if sysex is in progress and we get a status byte, it had
 | 
						|
           better be a realtime message or the starting SYSEX byte;
 | 
						|
           otherwise, we exit the sysex_in_progress state
 | 
						|
         */
 | 
						|
        if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK)) {
 | 
						|
            /* two choices: real-time or not. If it's real-time, then
 | 
						|
             * this should be delivered as a sysex byte because it is
 | 
						|
             * embedded in a sysex message
 | 
						|
             */
 | 
						|
            if (is_real_time(status)) {
 | 
						|
                midi->sysex_message |= 
 | 
						|
                        (status << (8 * midi->sysex_message_count++));
 | 
						|
                if (midi->sysex_message_count == 4) {
 | 
						|
                    pm_flush_sysex(midi, event->timestamp);
 | 
						|
                }
 | 
						|
            } else { /* otherwise, it's not real-time. This interrupts
 | 
						|
                      * a sysex message in progress */
 | 
						|
                midi->sysex_in_progress = FALSE;
 | 
						|
            }
 | 
						|
        } else if (Pm_Enqueue(midi->queue, event) == pmBufferOverflow) {
 | 
						|
            midi->sysex_in_progress = FALSE;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* pm_read_bytes -- read one (partial) sysex msg from MIDI data */
 | 
						|
/*
 | 
						|
 * returns how many bytes processed
 | 
						|
 */
 | 
						|
unsigned int pm_read_bytes(PmInternal *midi, unsigned char *data, 
 | 
						|
                    int len, PmTimestamp timestamp)
 | 
						|
{
 | 
						|
    int i = 0; /* index into data, must not be unsigned (!) */
 | 
						|
    PmEvent event;
 | 
						|
    event.timestamp = timestamp;
 | 
						|
    assert(midi);
 | 
						|
    /* note that since buffers may not have multiples of 4 bytes,
 | 
						|
     * pm_read_bytes may be called in the middle of an outgoing
 | 
						|
     * 4-byte PortMidi message. sysex_in_progress indicates that
 | 
						|
     * a sysex has been sent but no eox.
 | 
						|
     */
 | 
						|
    if (len == 0) return 0; /* sanity check */
 | 
						|
    if (!midi->sysex_in_progress) {
 | 
						|
        while (i < len) { /* process all data */
 | 
						|
            unsigned char byte = data[i++];
 | 
						|
            if (byte == MIDI_SYSEX &&
 | 
						|
                !pm_realtime_filtered(byte, midi->filters)) {
 | 
						|
                midi->sysex_in_progress = TRUE;
 | 
						|
                i--; /* back up so code below will get SYSEX byte */
 | 
						|
                break; /* continue looping below to process msg */
 | 
						|
            } else if (byte == MIDI_EOX) {
 | 
						|
                midi->sysex_in_progress = FALSE;
 | 
						|
                return i; /* done with one message */
 | 
						|
            } else if (byte & MIDI_STATUS_MASK) {
 | 
						|
                /* We're getting MIDI but no sysex in progress.
 | 
						|
                 * Either the SYSEX status byte was dropped or
 | 
						|
                 * the message was filtered. Drop the data, but
 | 
						|
                 * send any embedded realtime bytes.
 | 
						|
                 */
 | 
						|
                /* assume that this is a real-time message:
 | 
						|
                 * it is an error to pass non-real-time messages
 | 
						|
                 * to pm_read_bytes
 | 
						|
                 */
 | 
						|
                event.message = byte;
 | 
						|
                pm_read_short(midi, &event);
 | 
						|
            }
 | 
						|
        } /* all bytes in the buffer are processed */
 | 
						|
    }
 | 
						|
    /* Now, i<len implies sysex_in_progress. If sysex_in_progress
 | 
						|
     * becomes false in the loop, there must have been an overflow
 | 
						|
     * and we can just drop all remaining bytes 
 | 
						|
     */
 | 
						|
    while (i < len && midi->sysex_in_progress) {
 | 
						|
        if (midi->sysex_message_count == 0 && i <= len - 4 &&
 | 
						|
            ((event.message = (((long) data[i]) | 
 | 
						|
                             (((long) data[i+1]) << 8) |
 | 
						|
                             (((long) data[i+2]) << 16) |
 | 
						|
                             (((long) data[i+3]) << 24))) &
 | 
						|
             0x80808080) == 0) { /* all data, no status */ 
 | 
						|
            if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) {
 | 
						|
                midi->sysex_in_progress = FALSE;
 | 
						|
            }
 | 
						|
            i += 4;
 | 
						|
        } else {
 | 
						|
            while (i < len) {
 | 
						|
                /* send one byte at a time */
 | 
						|
                unsigned char byte = data[i++];
 | 
						|
                if (is_real_time(byte) && 
 | 
						|
                    pm_realtime_filtered(byte, midi->filters)) {
 | 
						|
                    continue; /* real-time data is filtered, so omit */
 | 
						|
                }
 | 
						|
                midi->sysex_message |= 
 | 
						|
                    (byte << (8 * midi->sysex_message_count++));
 | 
						|
                if (byte == MIDI_EOX) {
 | 
						|
                    midi->sysex_in_progress = FALSE;
 | 
						|
                    pm_flush_sysex(midi, event.timestamp);
 | 
						|
                    return i;
 | 
						|
                } else if (midi->sysex_message_count == 4) {
 | 
						|
                    pm_flush_sysex(midi, event.timestamp);
 | 
						|
                    /* after handling at least one non-data byte
 | 
						|
                     * and reaching a 4-byte message boundary,
 | 
						|
                     * resume trying to send 4 at a time in outer loop
 | 
						|
                     */
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return i;
 | 
						|
}
 | 
						|
 | 
						|
 |