Update of /cvsroot/pure-data/pd/portmidi/pm_common In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5166/pd/portmidi/pm_common
Modified Files: pminternal.h pmutil.c pmutil.h portmidi.c portmidi.h Log Message: 0.41-0 test 11
Index: pminternal.h =================================================================== RCS file: /cvsroot/pure-data/pd/portmidi/pm_common/pminternal.h,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** pminternal.h 15 Dec 2005 00:56:57 -0000 1.1 --- pminternal.h 16 Jan 2008 21:54:10 -0000 1.2 *************** *** 29,37 **** extern int pm_hosterror; extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; ! struct pm_internal_struct;
/* these do not use PmInternal because it is not defined yet... */ ! typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi, PmEvent *buffer); typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi, --- 29,37 ---- extern int pm_hosterror; extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; ! struct pm_internal_struct;
/* these do not use PmInternal because it is not defined yet... */ ! typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi, PmEvent *buffer); typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi, *************** *** 43,47 **** typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi, PmEvent *buffer); ! typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi); typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi); /* pm_open_fn should clean up all memory and close the device if any part --- 43,48 ---- typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi, PmEvent *buffer); ! typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi, ! PmTimestamp timestamp); typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi); /* pm_open_fn should clean up all memory and close the device if any part *************** *** 70,74 **** pm_close_fn close; /* close device */ pm_poll_fn poll; /* read pending midi events into portmidi buffer */ ! pm_has_host_error_fn has_host_error; /* true when device has had host error message */ pm_host_error_fn host_error; /* provide text readable host error message --- 71,75 ---- pm_close_fn close; /* close device */ pm_poll_fn poll; /* read pending midi events into portmidi buffer */ ! pm_has_host_error_fn has_host_error; /* true when device has had host error message */ pm_host_error_fn host_error; /* provide text readable host error message *************** *** 84,88 **** device closing (see PmDeviceInfo struct) */ void *descriptor; /* ID number passed to win32 multimedia API open */ ! void *internalDescriptor; /* points to PmInternal device, allows automatic device closing */ pm_fns_type dictionary; --- 85,89 ---- device closing (see PmDeviceInfo struct) */ void *descriptor; /* ID number passed to win32 multimedia API open */ ! void *internalDescriptor; /* points to PmInternal device, allows automatic device closing */ pm_fns_type dictionary; *************** *** 98,125 **** int device_id; /* which device is open (index to descriptors) */ short write_flag; /* MIDI_IN, or MIDI_OUT */ ! PmTimeProcPtr time_proc; /* where to get the time */ void *time_info; /* pass this to get_time() */ ! ! long buffer_len; /* how big is the buffer */ ! PmEvent *buffer; /* storage for: ! - midi input - midi output w/latency != 0 */ long head; long tail; ! long latency; /* time delay in ms between timestamps and actual output */ /* set to zero to get immediate, simple blocking output */ /* if latency is zero, timestamps will be ignored; */ /* if midi input device, this field ignored */ ! ! int overflow; /* set to non-zero if input is dropped */ ! int flush; /* flag to drop incoming sysex data because of overflow */ ! int sysex_in_progress; /* use for overflow management */ PmMessage sysex_message; /* buffer for 4 bytes of sysex data */ int sysex_message_count; /* how many bytes in sysex_message so far */
long filters; /* flags that filter incoming message classes */ ! int channel_mask; /* filter incoming messages based on channel */ PmTimestamp last_msg_time; /* timestamp of last message */ PmTimestamp sync_time; /* time of last synchronization */ --- 99,134 ---- int device_id; /* which device is open (index to descriptors) */ short write_flag; /* MIDI_IN, or MIDI_OUT */ ! PmTimeProcPtr time_proc; /* where to get the time */ void *time_info; /* pass this to get_time() */ ! long buffer_len; /* how big is the buffer or queue? */ ! #ifdef NEWBUFFER ! PmQueue *queue; ! #else ! PmEvent *buffer; /* storage for: ! - midi input - midi output w/latency != 0 */ long head; long tail; ! int overflow; /* set to non-zero if input is dropped */ ! #endif long latency; /* time delay in ms between timestamps and actual output */ /* set to zero to get immediate, simple blocking output */ /* if latency is zero, timestamps will be ignored; */ /* if midi input device, this field ignored */ ! ! int sysex_in_progress; /* when sysex status is seen, this flag becomes ! * true until EOX is seen. When true, new data is appended to the ! * stream of outgoing bytes. When overflow occurs, sysex data is ! * dropped (until an EOX or non-real-timei status byte is seen) so ! * that, if the overflow condition is cleared, we don't start ! * sending data from the middle of a sysex message. If a sysex ! * message is filtered, sysex_in_progress is false, causing the ! * message to be dropped. */ PmMessage sysex_message; /* buffer for 4 bytes of sysex data */ int sysex_message_count; /* how many bytes in sysex_message so far */
long filters; /* flags that filter incoming message classes */ ! int channel_mask; /* flter incoming messages based on channel */ PmTimestamp last_msg_time; /* timestamp of last message */ PmTimestamp sync_time; /* time of last synchronization */ *************** *** 128,164 **** pm_fns_type dictionary; /* implementation functions */ void *descriptor; /* system-dependent state */ ! } PmInternal;
- typedef struct { - long head; - long tail; - long len; - long msg_size; - long overflow; - char *buffer; - } PmQueueRep;
/* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */ ! void pm_init(void); ! void pm_term(void);
/* defined by portMidi, used by pmwinmm */ PmError none_write_short(PmInternal *midi, PmEvent *buffer); ! PmError none_sysex(PmInternal *midi, PmTimestamp timestamp); ! PmError none_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp); PmTimestamp none_synchronize(PmInternal *midi);
PmError pm_fail_fn(PmInternal *midi); PmError pm_success_fn(PmInternal *midi); PmError pm_add_device(char *interf, char *name, int input, void *descriptor, pm_fns_type dictionary); ! void pm_read_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp); ! void pm_begin_sysex(PmInternal *midi); ! void pm_end_sysex(PmInternal *midi); void pm_read_short(PmInternal *midi, PmEvent *event);
! #define none_write_flush pm_fail_fn #define none_poll pm_fail_fn #define success_poll pm_success_fn --- 137,173 ---- pm_fns_type dictionary; /* implementation functions */ void *descriptor; /* system-dependent state */ ! /* the following are used to expedite sysex data */ ! /* on windows, in debug mode, based on some profiling, these optimizations ! * cut the time to process sysex bytes from about 7.5 to 0.26 usec/byte, ! * but this does not count time in the driver, so I don't know if it is ! * important ! */ ! unsigned char *fill_base; /* addr of ptr to sysex data */ ! int *fill_offset_ptr; /* offset of next sysex byte */ ! int fill_length; /* how many sysex bytes to write */ } PmInternal;
/* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */ ! void pm_init(void); ! void pm_term(void);
/* defined by portMidi, used by pmwinmm */ PmError none_write_short(PmInternal *midi, PmEvent *buffer); ! PmError none_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp); PmTimestamp none_synchronize(PmInternal *midi);
PmError pm_fail_fn(PmInternal *midi); + PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp); PmError pm_success_fn(PmInternal *midi); PmError pm_add_device(char *interf, char *name, int input, void *descriptor, pm_fns_type dictionary); ! unsigned int pm_read_bytes(PmInternal *midi, unsigned char *data, int len, ! PmTimestamp timestamp); void pm_read_short(PmInternal *midi, PmEvent *event);
! #define none_write_flush pm_fail_timestamp_fn ! #define none_sysex pm_fail_timestamp_fn #define none_poll pm_fail_fn #define success_poll pm_success_fn
Index: pmutil.c =================================================================== RCS file: /cvsroot/pure-data/pd/portmidi/pm_common/pmutil.c,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** pmutil.c 15 Dec 2005 00:56:57 -0000 1.1 --- pmutil.c 16 Jan 2008 21:54:10 -0000 1.2 *************** *** 3,6 **** --- 3,7 ---- */ #include "stdlib.h" + #include "assert.h" #include "memory.h" #include "portmidi.h" *************** *** 8,30 **** #include "pminternal.h"
PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg) { PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep)); ! ! /* arg checking */ if (!queue) ! return NULL; ! ! queue->len = num_msgs * bytes_per_msg; ! queue->buffer = pm_alloc(queue->len); if (!queue->buffer) { pm_free(queue); return NULL; } queue->head = 0; queue->tail = 0; ! queue->msg_size = bytes_per_msg; queue->overflow = FALSE; return queue; } --- 9,73 ---- #include "pminternal.h"
+ #ifdef WIN32 + #define bzero(addr, siz) memset(addr, 0, siz) + #endif + + // #define QUEUE_DEBUG 1 + #ifdef QUEUE_DEBUG + #include "stdio.h" + #endif + + /* code is based on 4-byte words -- it should work on a 64-bit machine + as long as a "long" has 4 bytes. This code could be generalized to + be independent of the size of "long" */ + + typedef long int32; + + typedef struct { + long head; + long tail; + long len; + long msg_size; /* number of int32 in a message including extra word */ + long overflow; + long peek_overflow; + int32 *buffer; + int32 *peek; + int peek_flag; + } PmQueueRep; +
PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg) { PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep)); ! int int32s_per_msg = ((bytes_per_msg + sizeof(int32) - 1) & ! ~(sizeof(int32) - 1)) / sizeof(int32); ! /* arg checking */ if (!queue) ! return NULL; ! ! /* need extra word per message for non-zero encoding */ ! queue->len = num_msgs * (int32s_per_msg + 1); ! queue->buffer = (int32 *) pm_alloc(queue->len * sizeof(int32)); ! bzero(queue->buffer, queue->len * sizeof(int32)); if (!queue->buffer) { pm_free(queue); return NULL; + } else { /* allocate the "peek" buffer */ + queue->peek = (int32 *) pm_alloc(int32s_per_msg * sizeof(int32)); + if (!queue->peek) { + /* free everything allocated so far and return */ + pm_free(queue->buffer); + pm_free(queue); + return NULL; + } } + bzero(queue->buffer, queue->len * sizeof(int32)); queue->head = 0; queue->tail = 0; ! /* msg_size is in words */ ! queue->msg_size = int32s_per_msg + 1; /* note extra word is counted */ queue->overflow = FALSE; + queue->peek_overflow = FALSE; + queue->peek_flag = FALSE; return queue; } *************** *** 34,43 **** { PmQueueRep *queue = (PmQueueRep *) q; ! ! /* arg checking */ ! if (!queue || !queue->buffer) ! return pmBadPtr;
! pm_free(queue->buffer); pm_free(queue); return pmNoError; --- 77,87 ---- { PmQueueRep *queue = (PmQueueRep *) q; ! ! /* arg checking */ ! if (!queue || !queue->buffer || !queue->peek) ! return pmBadPtr;
! pm_free(queue->peek); ! pm_free(queue->buffer); pm_free(queue); return pmNoError; *************** *** 49,65 **** long head; PmQueueRep *queue = (PmQueueRep *) q;
! /* arg checking */ ! if(!queue) ! return pmBadPtr;
! if (queue->overflow) { ! queue->overflow = FALSE; return pmBufferOverflow; }
! head = queue->head; /* make sure this is written after access */ ! if (head == queue->tail) return 0; ! memcpy(msg, queue->buffer + head, queue->msg_size); head += queue->msg_size; if (head == queue->len) head = 0; --- 93,177 ---- long head; PmQueueRep *queue = (PmQueueRep *) q; + int i; + int32 *msg_as_int32 = (int32 *) msg;
! /* arg checking */ ! if (!queue) ! return pmBadPtr; ! /* a previous peek operation encountered an overflow, but the overflow ! * has not yet been reported to client, so do it now. No message is ! * returned, but on the next call, we will return the peek buffer. ! */ ! if (queue->peek_overflow) { ! queue->peek_overflow = FALSE; ! return pmBufferOverflow; ! } ! if (queue->peek_flag) { ! #ifdef QUEUE_DEBUG ! printf("Pm_Dequeue returns peek msg:"); ! for (i = 0; i < queue->msg_size - 1; i++) { ! printf(" %d", queue->peek[i]); ! } ! printf("\n"); ! #endif ! memcpy(msg, queue->peek, (queue->msg_size - 1) * sizeof(int32)); ! queue->peek_flag = FALSE; ! return 1; ! }
! head = queue->head; ! /* if writer overflows, it writes queue->overflow = tail+1 so that ! * when the reader gets to that position in the buffer, it can ! * return the overflow condition to the reader. The problem is that ! * at overflow, things have wrapped around, so tail == head, and the ! * reader will detect overflow immediately instead of waiting until ! * it reads everything in the buffer, wrapping around again to the ! * point where tail == head. So the condition also checks that ! * queue->buffer[head] is zero -- if so, then the buffer is now ! * empty, and we're at the point in the msg stream where overflow ! * occurred. It's time to signal overflow to the reader. If ! * queue->buffer[head] is non-zero, there's a message there and we ! * should read all the way around the buffer before signalling overflow. ! * There is a write-order dependency here, but to fail, the overflow ! * field would have to be written while an entire buffer full of ! * writes are still pending. I'm assuming out-of-order writes are ! * possible, but not that many. ! */ ! if (queue->overflow == head + 1 && !queue->buffer[head]) { ! queue->overflow = 0; /* non-overflow condition */ return pmBufferOverflow; }
! /* test to see if there is data in the queue -- test from back ! * to front so if writer is simultaneously writing, we don't ! * waste time discovering the write is not finished ! */ ! for (i = queue->msg_size - 1; i >= 0; i--) { ! if (!queue->buffer[head + i]) { ! return 0; ! } ! } ! #ifdef QUEUE_DEBUG ! printf("Pm_Dequeue:"); ! for (i = 0; i < queue->msg_size; i++) { ! printf(" %d", queue->buffer[head + i]); ! } ! printf("\n"); ! #endif ! memcpy(msg, (char *) &queue->buffer[head + 1], ! sizeof(int32) * (queue->msg_size - 1)); ! /* fix up zeros */ ! i = queue->buffer[head]; ! while (i < queue->msg_size) { ! int32 j; ! i--; /* msg does not have extra word so shift down */ ! j = msg_as_int32[i]; ! msg_as_int32[i] = 0; ! i = j; ! } ! /* signal that data has been removed by zeroing: */ ! bzero((char *) &queue->buffer[head], sizeof(int32) * queue->msg_size); ! ! /* update head */ head += queue->msg_size; if (head == queue->len) head = 0; *************** *** 69,132 ****
! /* source should not enqueue data if overflow is set */ ! /**/ PmError Pm_Enqueue(PmQueue *q, void *msg) { PmQueueRep *queue = (PmQueueRep *) q; long tail;
! /* arg checking */ ! if (!queue) ! return pmBadPtr;
! tail = queue->tail; ! memcpy(queue->buffer + tail, msg, queue->msg_size); ! tail += queue->msg_size; ! if (tail == queue->len) tail = 0; ! if (tail == queue->head) { ! queue->overflow = TRUE; ! /* do not update tail, so message is lost */ return pmBufferOverflow; } queue->tail = tail; return pmNoError; }
int Pm_QueueEmpty(PmQueue *q) { PmQueueRep *queue = (PmQueueRep *) q; if (!queue) return TRUE; ! return (queue->head == queue->tail); }
int Pm_QueueFull(PmQueue *q) { PmQueueRep *queue = (PmQueueRep *) q; ! long tail; ! ! /* arg checking */ ! if(!queue) ! return pmBadPtr; ! ! tail = queue->tail; ! tail += queue->msg_size; ! if (tail == queue->len) { ! tail = 0; } ! return (tail == queue->head); }
void *Pm_QueuePeek(PmQueue *q) { - long head; PmQueueRep *queue = (PmQueueRep *) q;
! /* arg checking */ ! if(!queue) ! return NULL;
! head = queue->head; /* make sure this is written after access */ ! if (head == queue->tail) return NULL; ! return queue->buffer + head; }
--- 181,311 ----
! ! PmError Pm_SetOverflow(PmQueue *q) ! { ! PmQueueRep *queue = (PmQueueRep *) q; ! long tail; ! /* no more enqueue until receiver acknowledges overflow */ ! if (queue->overflow) return pmBufferOverflow; ! if (!queue) ! return pmBadPtr; ! tail = queue->tail; ! queue->overflow = tail + 1; ! return pmBufferOverflow; ! } ! ! PmError Pm_Enqueue(PmQueue *q, void *msg) { PmQueueRep *queue = (PmQueueRep *) q; long tail; + int i; + int32 *src = (int32 *) msg; + int32 *ptr;
! int32 *dest;
! int rslt; ! /* no more enqueue until receiver acknowledges overflow */ ! if (!queue) return pmBadPtr; ! if (queue->overflow) return pmBufferOverflow; ! rslt = Pm_QueueFull(q); ! /* already checked above: if (rslt == pmBadPtr) return rslt; */ ! tail = queue->tail; ! if (rslt) { ! queue->overflow = tail + 1; return pmBufferOverflow; } + + /* queue is has room for message, and overflow flag is cleared */ + ptr = &queue->buffer[tail]; + dest = ptr + 1; + for (i = 1; i < queue->msg_size; i++) { + int32 j = src[i - 1]; + if (!j) { + *ptr = i; + ptr = dest; + } else { + *dest = j; + } + dest++; + } + *ptr = i; + #ifdef QUEUE_DEBUG + printf("Pm_Enqueue:"); + for (i = 0; i < queue->msg_size; i++) { + printf(" %d", queue->buffer[tail + i]); + } + printf("\n"); + #endif + tail += queue->msg_size; + if (tail == queue->len) tail = 0; queue->tail = tail; return pmNoError; }
+ int Pm_QueueEmpty(PmQueue *q) { PmQueueRep *queue = (PmQueueRep *) q; if (!queue) return TRUE; ! return (queue->buffer[queue->head] == 0); }
+ int Pm_QueueFull(PmQueue *q) { PmQueueRep *queue = (PmQueueRep *) q; ! int tail; ! int i; ! /* arg checking */ ! if (!queue) ! return pmBadPtr; ! tail = queue->tail; ! /* test to see if there is space in the queue */ ! for (i = 0; i < queue->msg_size; i++) { ! if (queue->buffer[tail + i]) { ! return TRUE; ! } } ! return FALSE; }
void *Pm_QueuePeek(PmQueue *q) { PmQueueRep *queue = (PmQueueRep *) q; + PmError rslt; + long temp;
! /* arg checking */ ! if (!queue) ! return NULL;
! if (queue->peek_flag) { ! return queue->peek; ! } ! /* this is ugly: if peek_overflow is set, then Pm_Dequeue() ! * returns immediately with pmBufferOverflow, but here, we ! * want Pm_Dequeue() to really check for data. If data is ! * there, we can return it ! */ ! temp = queue->peek_overflow; ! queue->peek_overflow = FALSE; ! rslt = Pm_Dequeue(q, queue->peek); ! queue->peek_overflow = temp; ! ! if (rslt == 1) { ! queue->peek_flag = TRUE; ! return queue->peek; ! } else if (rslt == pmBufferOverflow) { ! /* when overflow is indicated, the queue is empty and the ! * first message that was dropped by Enqueue (signalling ! * pmBufferOverflow to its caller) would have been the next ! * message in the queue. Pm_QueuePeek will return NULL, but ! * remember that an overflow occurred. (see Pm_Dequeue) ! */ ! queue->peek_overflow = TRUE; ! } ! return NULL; }
Index: portmidi.c =================================================================== RCS file: /cvsroot/pure-data/pd/portmidi/pm_common/portmidi.c,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** portmidi.c 15 Dec 2005 00:56:57 -0000 1.1 --- portmidi.c 16 Jan 2008 21:54:10 -0000 1.2 *************** *** 3,6 **** --- 3,9 ---- #include "portmidi.h" #include "porttime.h" + #ifdef NEWBUFFER + #include "pmutil.h" + #endif #include "pminternal.h" #include <assert.h> *************** *** 32,36 **** [...1617 lines suppressed...] *************** *** 972,980 **** /* arg checking */ assert(midi != NULL); ! assert(!Pm_HasHostError(midi)); ! tail = midi->tail + 1; if (tail == midi->buffer_len) tail = 0; return tail == midi->head; } ! --- 1177,1184 ---- /* arg checking */ assert(midi != NULL); ! tail = midi->tail + 1; if (tail == midi->buffer_len) tail = 0; return tail == midi->head; } ! #endif
Index: portmidi.h =================================================================== RCS file: /cvsroot/pure-data/pd/portmidi/pm_common/portmidi.h,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** portmidi.h 15 Dec 2005 00:56:57 -0000 1.1 --- portmidi.h 16 Jan 2008 21:54:10 -0000 1.2 *************** *** 11,15 **** * * Copyright (c) 1999-2000 Ross Bencina and Phil Burk ! * Copyright (c) 2001 Roger B. Dannenberg * * Permission is hereby granted, free of charge, to any person obtaining --- 11,20 ---- * * Copyright (c) 1999-2000 Ross Bencina and Phil Burk ! * Copyright (c) 2001-2006 Roger B. Dannenberg ! * ! * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/ ! * ! * Copyright (c) 1999-2000 Ross Bencina and Phil Burk ! * Copyright (c) 2001-2006 Roger B. Dannenberg * * Permission is hereby granted, free of charge, to any person obtaining *************** *** 24,31 **** * included in all copies or substantial portions of the Software. * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF --- 29,32 ---- *************** *** 35,146 **** * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */
/* CHANGELOG FOR PORTMIDI ! * ! * 15Nov04 Ben Allison ! * - sysex output now uses one buffer/message and reallocates buffer ! * - if needed ! * - filters expanded for many message types and channels ! * - detailed changes are as follows: ! * ------------- in pmwinmm.c -------------- ! * - new #define symbol: OUTPUT_BYTES_PER_BUFFER ! * - change SYSEX_BYTES_PER_BUFFER to 1024 ! * - added MIDIHDR_BUFFER_LENGTH(x) to correctly count midihdr buffer length ! * - change MIDIHDR_SIZE(x) to (MIDIHDR_BUFFER_LENGTH(x) + sizeof(MIDIHDR)) ! * - change allocate_buffer to use new MIDIHDR_BUFFER_LENGTH macro ! * - new macros for MIDIHDR_SYSEX_SIZE and MIDIHDR_SYSEX_BUFFER_LENGTH ! * - similar to above, but counts appropriately for sysex messages ! * - added the following members to midiwinmm_struct for sysex data: ! * - LPMIDIHDR *sysex_buffers; ** pool of buffers for sysex data ** ! * - int num_sysex_buffers; ** how many sysex buffers ** ! * - int next_sysex_buffer; ** index of next sysexbuffer to send ** ! * - HANDLE sysex_buffer_signal; ** to wait for free sysex buffer ** ! * - duplicated allocate_buffer, alocate_buffers and get_free_output_buffer ! * - into equivalent sysex_buffer form ! * - changed winmm_in_open to initialize new midiwinmm_struct members and ! * - to use the new allocate_sysex_buffer() function instead of ! * - allocate_buffer() ! * - changed winmm_out_open to initialize new members, create sysex buffer ! * - signal, and allocate 2 sysex buffers ! * - changed winmm_out_delete to free sysex buffers and shut down the sysex ! * - buffer signal ! * - create new function resize_sysex_buffer which resizes m->hdr to the ! * - passed size, and corrects the midiwinmm_struct accordingly. ! * - changed winmm_write_byte to use new resize_sysex_buffer function, ! * - if resize fails, write current buffer to output and continue ! * - changed winmm_out_callback to use buffer_signal or sysex_buffer_signal ! * - depending on which buffer was finished ! * ------------- in portmidi.h -------------- ! * - added pmBufferMaxSize to PmError to indicate that the buffer would be ! * - too large for the underlying API ! * - added additional filters ! * - added prototype, documentation, and helper macro for Pm_SetChannelMask ! * ------------- in portmidi.c -------------- ! * - added pm_status_filtered() and pm_realtime_filtered() functions to ! * separate filtering logic from buffer logic in pm_read_short ! * - added Pm_SetChannelMask function ! * - added pm_channel_filtered() function ! * ------------- in pminternal.h -------------- ! * - added member to PortMidiStream for channel mask ! * ! * 25May04 RBD ! * - removed support for MIDI THRU ! * - moved filtering from Pm_Read to pm_enqueue to avoid buffer ovfl ! * - extensive work on Mac OS X port, especially sysex and error handling ! * ! * 18May04 RBD ! * - removed side-effects from assert() calls. Now you can disable assert(). ! * - no longer check pm_hosterror everywhere, fixing a bug where an open ! * failure could cause a write not to work on a previously opened port ! * until you call Pm_GetHostErrorText(). ! * 16May04 RBD and Chris Roberts ! * - Some documentation wordsmithing in portmidi.h ! * - Dynamically allocate port descriptor structures ! * - Fixed parameter error in midiInPrepareBuffer and midiInAddBuffer. ! * ! * 09Oct03 RBD ! * - Changed Thru handling. Now the client does all the work and the client ! * must poll or read to keep thru messages flowing. ! * ! * 31May03 RBD ! * - Fixed various bugs. ! * - Added linux ALSA support with help from Clemens Ladisch ! * - Added Mac OS X support, implemented by Jon Parise, updated and ! * integrated by Andrew Zeldis and Zico Kolter ! * - Added latency program to build histogram of system latency using PortTime. ! * ! * 30Jun02 RBD Extensive rewrite of sysex handling. It works now. ! * Extensive reworking of error reporting and error text -- no ! * longer use dictionary call to delete data; instead, Pm_Open ! * and Pm_Close clean up before returning an error code, and ! * error text is saved in a system-independent location. ! * Wrote sysex.c to test sysex message handling. ! * ! * 15Jun02 BCT changes: ! * - Added pmHostError text handling. ! * - For robustness, check PortMidi stream args not NULL. ! * - Re-C-ANSI-fied code (changed many C++ comments to C style) ! * - Reorganized code in pmwinmm according to input/output functionality (made ! * cleanup handling easier to reason about) ! * - Fixed Pm_Write calls (portmidi.h says these should not return length but Pm_Error) ! * - Cleaned up memory handling (now system specific data deleted via dictionary ! * call in PortMidi, allows client to query host errors). ! * - Added explicit asserts to verify various aspects of pmwinmm implementation behaves as ! * logic implies it should. Specifically: verified callback routines not reentrant and ! * all verified status for all unchecked Win32 MMedia API calls perform successfully ! * - Moved portmidi initialization and clean-up routines into DLL to fix Win32 MMedia API ! * bug (i.e. if devices not explicitly closed, must reboot to debug application further). ! * With this change, clients no longer need explicitly call Pm_Initialize, Pm_Terminate, or ! * explicitly Pm_Close open devices when using WinMM version of PortMidi. ! * ! * 23Jan02 RBD Fixed bug in pmwinmm.c thru handling ! * ! * 21Jan02 RBD Added tests in Pm_OpenInput() and Pm_OpenOutput() to prevent ! * opening an input as output and vice versa. ! * Added comments and documentation. ! * Implemented Pm_Terminate(). ! * * * IMPORTANT INFORMATION ABOUT A WIN32 BUG: --- 36,54 ---- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + /* + * The text above constitutes the entire PortMidi license; however, + * the PortMusic community also makes the following non-binding requests: * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. */
/* CHANGELOG FOR PORTMIDI ! * (see ../CHANGELOG.txt) * * IMPORTANT INFORMATION ABOUT A WIN32 BUG: *************** *** 150,156 **** * * If client exits for example with: ! * i) assert ! * ii) Ctrl^c, ! * then DLL clean-up routine called. However, when client does something * really bad (e.g. assigns value to NULL pointer) then DLL CLEANUP ROUTINE * NEVER RUNS! In this state, if you wait around long enough, you will --- 58,64 ---- * * If client exits for example with: ! * i) assert ! * ii) Ctrl^c, ! * then DLL clean-up routine called. However, when client does something * really bad (e.g. assigns value to NULL pointer) then DLL CLEANUP ROUTINE * NEVER RUNS! In this state, if you wait around long enough, you will *************** *** 158,162 **** * exist zombie process that you can't kill. * ! * NOTES ON HOST ERROR REPORTING: * * PortMidi errors (of type PmError) are generic, system-independent errors. --- 66,74 ---- * exist zombie process that you can't kill. * ! * You can enable the DLL cleanup routine by defining USE_DLL_FOR_CLEANUP. ! * Do not define this preprocessor symbol if you do not want to use this ! * feature. ! * ! * NOTES ON HOST ERROR REPORTING: * * PortMidi errors (of type PmError) are generic, system-independent errors. *************** *** 166,181 **** * get more information by calling Pm_HasHostError() to test if there is * a pending host error, and Pm_GetHostErrorText() to get a text string ! * describing the error. Host errors are reported on a per-device basis ! * because only after you open a device does PortMidi have a place to ! * record the host error code. I.e. only ! * those routines that receive a (PortMidiStream *) argument check and ! * report errors. One exception to this is that Pm_OpenInput() and * Pm_OpenOutput() can report errors even though when an error occurs, * there is no PortMidiStream* to hold the error. Fortunately, both * of these functions return any error immediately, so we do not really * need per-device error memory. Instead, any host error code is stored ! * in a global, pmHostError is returned, and the user can call * Pm_GetHostErrorText() to get the error message (and the invalid stream ! * parameter will be ignored.) The functions * pm_init and pm_term do not fail or raise * errors. The job of pm_init is to locate all available devices so that --- 78,93 ---- * get more information by calling Pm_HasHostError() to test if there is * a pending host error, and Pm_GetHostErrorText() to get a text string ! * describing the error. Host errors are reported on a per-device basis ! * because only after you open a device does PortMidi have a place to ! * record the host error code. I.e. only ! * those routines that receive a (PortMidiStream *) argument check and ! * report errors. One exception to this is that Pm_OpenInput() and * Pm_OpenOutput() can report errors even though when an error occurs, * there is no PortMidiStream* to hold the error. Fortunately, both * of these functions return any error immediately, so we do not really * need per-device error memory. Instead, any host error code is stored ! * in a global, pmHostError is returned, and the user can call * Pm_GetHostErrorText() to get the error message (and the invalid stream ! * parameter will be ignored.) The functions * pm_init and pm_term do not fail or raise * errors. The job of pm_init is to locate all available devices so that *************** *** 184,213 **** * * Host errors come in two flavors: ! * a) host error * b) host error during callback ! * These can occur w/midi input or output devices. (b) can only happen * asynchronously (during callback routines), whereas (a) only occurs while ! * synchronously running PortMidi and any resulting system dependent calls * ! * Host-error reporting relies on following assumptions: ! * 1) PortMidi routines won't allow system dependent routines to be ! * called when args are bogus. ! * Thus, in pmwinmm.c it is safe to assume: ! * - stream ptr valid ! * - currently not operating in "has host error" state ! * 2) Host-error reporting relies on a staged delivery of error messages. ! * When a host error occurs, the error code is saved with the stream. ! * The error is reported as a return code from the next operation on ! * the stream. This could be immediately if the error is synchronous, ! * or delayed if the error is an asynchronous callback problem. In ! * any case, when pmHostError is returned, the error is copied to ! * a global, pm_hosterror and the error code stored with the stream ! * is cleared. If the user chooses to inquire about the error using ! * Pm_GetHostErrorText(), the error will be reported as text. If the ! * user ignores the error and makes another call on the stream, the ! * call will proceed because the error code associated with the stream ! * has been cleared. * ! */
#ifndef FALSE --- 96,120 ---- * * Host errors come in two flavors: ! * a) host error * b) host error during callback ! * These can occur w/midi input or output devices. (b) can only happen * asynchronously (during callback routines), whereas (a) only occurs while ! * synchronously running PortMidi and any resulting system dependent calls. ! * Both (a) and (b) are reported by the next read or write call. You can ! * also query for asynchronous errors (b) at any time by calling ! * Pm_HasHostError(). * ! * NOTES ON COMPILE-TIME SWITCHES * ! * DEBUG assumes stdio and a console. Use this if you want automatic, simple ! * error reporting, e.g. for prototyping. If you are using MFC or some ! * other graphical interface with no console, DEBUG probably should be ! * undefined. ! * PM_CHECK_ERRORS more-or-less takes over error checking for return values, ! * stopping your program and printing error messages when an error ! * occurs. This also uses stdio for console text I/O. ! * USE_DLL_FOR_CLEANUP is described above. (Windows only.) ! * ! */
#ifndef FALSE *************** *** 225,229 **** pmNoError = 0, pmHostError = -10000, ! pmInvalidDeviceId, /* out of range or output device when input is requested or vice versa */ pmInsufficientMemory, pmBufferTooSmall, --- 132,140 ---- pmNoError = 0, pmHostError = -10000, ! pmInvalidDeviceId, /* out of range or ! * output device when input is requested or ! * input device when output is requested or ! * device is already opened ! */ pmInsufficientMemory, pmBufferTooSmall, *************** *** 232,236 **** pmBadData, /* illegal midi data, e.g. missing EOX */ pmInternalError, ! pmBufferMaxSize, /* buffer is already as large as it can be */ } PmError;
--- 143,148 ---- pmBadData, /* illegal midi data, e.g. missing EOX */ pmInternalError, ! pmBufferMaxSize /* buffer is already as large as it can be */ ! /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */ } PmError;
*************** *** 256,267 **** /* Test whether stream has a pending host error. Normally, the client finds ! out about errors through returned error codes, but some errors can occur ! asynchronously where the client does not ! explicitly call a function, and therefore cannot receive an error code. ! The client can test for a pending error using Pm_HasHostError(). If true, ! the error can be accessed and cleared by calling Pm_GetErrorText(). The ! client does not need to call Pm_HasHostError(). Any pending error will be ! reported the next time the client performs an explicit function call on ! the stream, e.g. an input or output operation. */ int Pm_HasHostError( PortMidiStream * stream ); --- 168,182 ---- /* Test whether stream has a pending host error. Normally, the client finds ! out about errors through returned error codes, but some errors can occur ! asynchronously where the client does not ! explicitly call a function, and therefore cannot receive an error code. ! The client can test for a pending error using Pm_HasHostError(). If true, ! the error can be accessed and cleared by calling Pm_GetErrorText(). ! Errors are also cleared by calling other functions that can return ! errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The ! client does not need to call Pm_HasHostError(). Any pending error will be ! reported the next time the client performs an explicit function call on ! the stream, e.g. an input or output operation. Until the error is cleared, ! no new error codes will be obtained, even for a different stream. */ int Pm_HasHostError( PortMidiStream * stream ); *************** *** 269,274 ****
/* Translate portmidi error number into human readable message. ! These strings are constants (set at compile time) so client has ! no need to allocate storage */ const char *Pm_GetErrorText( PmError errnum ); --- 184,189 ----
/* Translate portmidi error number into human readable message. ! These strings are constants (set at compile time) so client has ! no need to allocate storage */ const char *Pm_GetErrorText( PmError errnum ); *************** *** 276,285 **** /* Translate portmidi host error into human readable message. These strings are computed at run time, so client has to allocate storage. ! After this routine executes, the host error is cleared. */ void Pm_GetHostErrorText(char * msg, unsigned int len);
#define HDRLENGTH 50 ! #define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less than this number of characters */
--- 191,200 ---- /* Translate portmidi host error into human readable message. These strings are computed at run time, so client has to allocate storage. ! After this routine executes, the host error is cleared. */ void Pm_GetHostErrorText(char * msg, unsigned int len);
#define HDRLENGTH 50 ! #define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less than this number of characters */
*************** *** 293,297 **** #define pmNoDevice -1 typedef struct { ! int structVersion; const char *interf; /* underlying MIDI API, e.g. MMSystem or DirectX */ const char *name; /* device name, e.g. USB MidiSport 1x1 */ --- 208,212 ---- #define pmNoDevice -1 typedef struct { ! int structVersion; const char *interf; /* underlying MIDI API, e.g. MMSystem or DirectX */ const char *name; /* device name, e.g. USB MidiSport 1x1 */ *************** *** 309,318 **** Return the default device ID or pmNoDevice if there are no devices. The result can be passed to Pm_OpenMidi(). ! On the PC, the user can specify a default device by setting an environment variable. For example, to use device #1.
set PM_RECOMMENDED_OUTPUT_DEVICE=1 ! The user should first determine the available device ID by using the supplied application "testin" or "testout". --- 224,233 ---- Return the default device ID or pmNoDevice if there are no devices. The result can be passed to Pm_OpenMidi(). ! On the PC, the user can specify a default device by setting an environment variable. For example, to use device #1.
set PM_RECOMMENDED_OUTPUT_DEVICE=1 ! The user should first determine the available device ID by using the supplied application "testin" or "testout". *************** *** 328,332 **** for a string. The number of the first device with a substring that matches the string exactly is returned. For example, if the string ! in the registry is "USB", and device 1 is named "In USB MidiSport 1x1", then that will be the default input because it contains the string "USB". --- 243,247 ---- for a string. The number of the first device with a substring that matches the string exactly is returned. For example, if the string ! in the registry is "USB", and device 1 is named "In USB MidiSport 1x1", then that will be the default input because it contains the string "USB". *************** *** 334,350 **** In addition to the name, PmDeviceInfo has the member "interf", which is the interface name. (The "interface" is the underlying software ! system or API used by PortMidi to access devices. Examples are ! MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) ! At present, the only Win32 interface is "MMSystem", the only Linux ! interface is "ALSA", and the only Max OS X interface is "CoreMIDI". To specify both the interface and the device name in the registry, separate the two with a comma and a space, e.g.: MMSystem, In USB MidiSport 1x1 In this case, the string before the comma must be a substring of ! the "interf" string, and the string after the space must be a substring of the "name" name string in order to match the device.
Note: in the current release, the default is simply the first device ! (the input or output device with the lowest PmDeviceID). */ PmDeviceID Pm_GetDefaultInputDeviceID( void ); --- 249,265 ---- In addition to the name, PmDeviceInfo has the member "interf", which is the interface name. (The "interface" is the underlying software ! system or API used by PortMidi to access devices. Examples are ! MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) ! At present, the only Win32 interface is "MMSystem", the only Linux ! interface is "ALSA", and the only Max OS X interface is "CoreMIDI". To specify both the interface and the device name in the registry, separate the two with a comma and a space, e.g.: MMSystem, In USB MidiSport 1x1 In this case, the string before the comma must be a substring of ! the "interf" string, and the string after the space must be a substring of the "name" name string in order to match the device.
Note: in the current release, the default is simply the first device ! (the input or output device with the lowest PmDeviceID). */ PmDeviceID Pm_GetDefaultInputDeviceID( void ); *************** *** 392,404 **** outputDriverInfo should be NULL.
! For input, the buffersize specifies the number of input events to be ! buffered waiting to be read using Pm_Read(). For output, buffersize ! specifies the number of output events to be buffered waiting for output. (In some cases -- see below -- PortMidi does not buffer output at all ! and merely passes data to a lower-level API, in which case buffersize ! is ignored.) ! ! latency is the delay in milliseconds applied to timestamps to determine ! when the output should actually occur. (If latency is < 0, 0 is assumed.) If latency is zero, timestamps are ignored and all output is delivered immediately. If latency is greater than zero, output is delayed until --- 307,319 ---- outputDriverInfo should be NULL.
! For input, the buffersize specifies the number of input events to be ! buffered waiting to be read using Pm_Read(). For output, buffersize ! specifies the number of output events to be buffered waiting for output. (In some cases -- see below -- PortMidi does not buffer output at all ! and merely passes data to a lower-level API, in which case buffersize ! is ignored.) ! ! latency is the delay in milliseconds applied to timestamps to determine ! when the output should actually occur. (If latency is < 0, 0 is assumed.) If latency is zero, timestamps are ignored and all output is delivered immediately. If latency is greater than zero, output is delayed until *************** *** 406,415 **** to the time source indicated by time_proc. Timestamps are absolute, not relative delays or offsets.) In some cases, PortMidi can obtain ! better timing than your application by passing timestamps along to the ! device driver or hardware. Latency may also help you to synchronize midi ! data to audio data by matching midi latency to the audio buffer latency.
time_proc is a pointer to a procedure that returns time in milliseconds. It ! may be NULL, in which case a default millisecond timebase (PortTime) is used. If the application wants to use PortTime, it should start the timer (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the --- 321,330 ---- to the time source indicated by time_proc. Timestamps are absolute, not relative delays or offsets.) In some cases, PortMidi can obtain ! better timing than your application by passing timestamps along to the ! device driver or hardware. Latency may also help you to synchronize midi ! data to audio data by matching midi latency to the audio buffer latency.
time_proc is a pointer to a procedure that returns time in milliseconds. It ! may be NULL, in which case a default millisecond timebase (PortTime) is used. If the application wants to use PortTime, it should start the timer (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the *************** *** 420,424 **** times are used to schedule outgoing MIDI data (when latency is non-zero).
! time_info is a pointer passed to time_proc.
return value: --- 335,339 ---- times are used to schedule outgoing MIDI data (when latency is non-zero).
! time_info is a pointer passed to time_proc.
return value: *************** *** 429,433 ****
Any stream that is successfully opened should eventually be closed ! by calling Pm_Close().
*/ --- 344,348 ----
Any stream that is successfully opened should eventually be closed ! by calling Pm_Close().
*/ *************** *** 447,451 **** long latency );
! /* Pm_SetFilter() sets filters on an open input stream to drop selected input types. By default, only active sensing messages are filtered. --- 362,366 ---- long latency );
! /* Pm_SetFilter() sets filters on an open input stream to drop selected input types. By default, only active sensing messages are filtered. *************** *** 462,505 ****
/* filter active sensing messages (0xFE): */ ! #define PM_FILT_ACTIVE 0x1 /* filter system exclusive messages (0xF0): */ ! #define PM_FILT_SYSEX 0x2 ! /* filter clock messages (0xF8 only, does not filter clock start, etc.): */ ! #define PM_FILT_CLOCK 0x4 /* filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ ! #define PM_FILT_PLAY 0x8 ! /* filter undefined F9 messages (some equipment uses this as a 10ms 'tick') */ ! #define PM_FILT_F9 0x10 ! #define PM_FILT_TICK PM_FILT_F9 /* filter undefined FD messages */ ! #define PM_FILT_FD 0x20 /* filter undefined real-time messages */ ! #define PM_FILT_UNDEFINED (PM_FILT_F9 | PM_FILT_FD) /* filter reset messages (0xFF) */ ! #define PM_FILT_RESET 0x40 /* filter all real-time messages */ ! #define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET) /* filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ ! #define PM_FILT_NOTE 0x80 /* filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ ! #define PM_FILT_CHANNEL_AFTERTOUCH 0x100 ! /* per-note aftertouch (Ensoniq holds a patent on generating this on keyboards until June 2006) (0xA0-0xAF) */ ! #define PM_FILT_POLY_AFTERTOUCH 0x200 /* filter both channel and poly aftertouch */ #define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) /* Program changes (0xC0-0xCF) */ ! #define PM_FILT_PROGRAM 0x400 /* Control Changes (CC's) (0xB0-0xBF)*/ ! #define PM_FILT_CONTROL 0x800 /* Pitch Bender (0xE0-0xEF*/ ! #define PM_FILT_PITCHBEND 0x1000 /* MIDI Time Code (0xF1)*/ ! #define PM_FILT_MTC 0x2000 /* Song Position (0xF2) */ ! #define PM_FILT_SONG_POSITION 0x4000 /* Song Select (0xF3)*/ ! #define PM_FILT_SONG_SELECT 0x8000 /* Tuning request (0xF6)*/ ! #define PM_FILT_TUNE 0x10000 /* All System Common messages (mtc, song position, song select, tune request) */ #define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) --- 377,420 ----
/* filter active sensing messages (0xFE): */ ! #define PM_FILT_ACTIVE (1 << 0x0E) /* filter system exclusive messages (0xF0): */ ! #define PM_FILT_SYSEX (1 << 0x00) ! /* filter clock messages (CLOCK 0xF8, START 0xFA, STOP 0xFC, and CONTINUE 0xFB) */ ! #define PM_FILT_CLOCK ((1 << 0x08) | (1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) /* filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ ! #define PM_FILT_PLAY (1 << 0x0A) ! /* filter tick messages (0xF9) */ ! #define PM_FILT_TICK (1 << 0x09) /* filter undefined FD messages */ ! #define PM_FILT_FD (1 << 0x0D) /* filter undefined real-time messages */ ! #define PM_FILT_UNDEFINED PM_FILT_FD /* filter reset messages (0xFF) */ ! #define PM_FILT_RESET (1 << 0x0F) /* filter all real-time messages */ ! #define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ ! PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) /* filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ ! #define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) /* filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ ! #define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) ! /* per-note aftertouch (0xA0-0xAF) */ ! #define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) /* filter both channel and poly aftertouch */ #define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) /* Program changes (0xC0-0xCF) */ ! #define PM_FILT_PROGRAM (1 << 0x1C) /* Control Changes (CC's) (0xB0-0xBF)*/ ! #define PM_FILT_CONTROL (1 << 0x1B) /* Pitch Bender (0xE0-0xEF*/ ! #define PM_FILT_PITCHBEND (1 << 0x1E) /* MIDI Time Code (0xF1)*/ ! #define PM_FILT_MTC (1 << 0x01) /* Song Position (0xF2) */ ! #define PM_FILT_SONG_POSITION (1 << 0x02) /* Song Select (0xF3)*/ ! #define PM_FILT_SONG_SELECT (1 << 0x03) /* Tuning request (0xF6)*/ ! #define PM_FILT_TUNE (1 << 0x06) /* All System Common messages (mtc, song position, song select, tune request) */ #define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) *************** *** 508,512 **** PmError Pm_SetFilter( PortMidiStream* stream, long filters );
- /* Pm_SetChannelMask() filters incoming messages based on channel. --- 423,426 ---- *************** *** 533,541 **** */ PmError Pm_Abort( PortMidiStream* stream ); ! /* Pm_Close() closes a midi stream, flushing any pending buffers. ! (PortMidi attempts to close open streams when the application ! exits -- this is particularly difficult under Windows.) */ PmError Pm_Close( PortMidiStream* stream ); --- 447,455 ---- */ PmError Pm_Abort( PortMidiStream* stream ); ! /* Pm_Close() closes a midi stream, flushing any pending buffers. ! (PortMidi attempts to close open streams when the application ! exits -- this is particularly difficult under Windows.) */ PmError Pm_Close( PortMidiStream* stream ); *************** *** 545,549 **** and/or data2 are not present, use zero.
! Pm_MessageStatus(), Pm_MessageData1(), and Pm_MessageData2() extract fields from a long-encoded midi message. */ --- 459,463 ---- and/or data2 are not present, use zero.
! Pm_MessageStatus(), Pm_MessageData1(), and Pm_MessageData2() extract fields from a long-encoded midi message. */ *************** *** 561,576 **** PmEvent carries the status byte.
! Note that MIDI allows nested messages: the so-called "real-time" MIDI ! messages can be inserted into the MIDI byte stream at any location, including within a sysex message. MIDI real-time messages are one-byte ! messages used mainly for timing (see the MIDI spec). PortMidi retains ! the order of non-real-time MIDI messages on both input and output, but it does not specify exactly how real-time messages are processed. This ! is particulary problematic for MIDI input, because the input parser ! must either prepare to buffer an unlimited number of sysex message ! bytes or to buffer an unlimited number of real-time messages that arrive embedded in a long sysex message. To simplify things, the input ! parser is allowed to pass real-time MIDI messages embedded within a ! sysex message, and it is up to the client to detect, process, and remove these messages as they arrive.
--- 475,490 ---- PmEvent carries the status byte.
! Note that MIDI allows nested messages: the so-called "real-time" MIDI ! messages can be inserted into the MIDI byte stream at any location, including within a sysex message. MIDI real-time messages are one-byte ! messages used mainly for timing (see the MIDI spec). PortMidi retains ! the order of non-real-time MIDI messages on both input and output, but it does not specify exactly how real-time messages are processed. This ! is particulary problematic for MIDI input, because the input parser ! must either prepare to buffer an unlimited number of sysex message ! bytes or to buffer an unlimited number of real-time messages that arrive embedded in a long sysex message. To simplify things, the input ! parser is allowed to pass real-time MIDI messages embedded within a ! sysex message, and it is up to the client to detect, process, and remove these messages as they arrive.
*************** *** 578,588 **** by either an EOX status byte (anywhere in the 4 byte messages) or by a non-real-time status byte in the low order byte of the message. ! If you get a non-real-time status byte but there was no EOX byte, it means the sysex message was somehow truncated. This is not considered an error; e.g., a missing EOX can result from the user disconnecting a MIDI cable during sysex transmission.
! A real-time message can occur within a sysex message. A real-time ! message will always occupy a full PmEvent with the status byte in the low-order byte of the PmEvent message field. (This implies that the byte-order of sysex bytes and real-time message bytes may not --- 492,502 ---- by either an EOX status byte (anywhere in the 4 byte messages) or by a non-real-time status byte in the low order byte of the message. ! If you get a non-real-time status byte but there was no EOX byte, it means the sysex message was somehow truncated. This is not considered an error; e.g., a missing EOX can result from the user disconnecting a MIDI cable during sysex transmission.
! A real-time message can occur within a sysex message. A real-time ! message will always occupy a full PmEvent with the status byte in the low-order byte of the PmEvent message field. (This implies that the byte-order of sysex bytes and real-time message bytes may not *************** *** 591,619 **** first. The first word of the sysex message will be delivered only after the 4th byte arrives, filling the 4-byte PmEvent message field. ! The timestamp field is observed when the output port is opened with a non-zero latency. A timestamp of zero means "use the current time", which in turn means to deliver the message with a delay of latency (the latency parameter used when opening the output port.) ! Do not expect PortMidi to sort data according to timestamps -- ! messages should be sent in the correct order, and timestamps MUST be non-decreasing.
! A sysex message will generally fill many PmEvent structures. On output to a PortMidiStream with non-zero latency, the first timestamp ! on sysex message data will determine the time to begin sending the ! message. PortMidi implementations may ignore timestamps for the ! remainder of the sysex message. ! ! On input, the timestamp ideally denotes the arrival time of the ! status byte of the message. The first timestamp on sysex message ! data will be valid. Subsequent timestamps may denote ! when message bytes were actually received, or they may be simply copies of the first timestamp.
! Timestamps for nested messages: If a real-time message arrives in ! the middle of some other message, it is enqueued immediately with ! the timestamp corresponding to its arrival time. The interrupted ! non-real-time message or 4-byte packet of sysex data will be enqueued later. The timestamp of interrupted data will be equal to that of the interrupting real-time message to insure that timestamps are --- 505,533 ---- first. The first word of the sysex message will be delivered only after the 4th byte arrives, filling the 4-byte PmEvent message field. ! The timestamp field is observed when the output port is opened with a non-zero latency. A timestamp of zero means "use the current time", which in turn means to deliver the message with a delay of latency (the latency parameter used when opening the output port.) ! Do not expect PortMidi to sort data according to timestamps -- ! messages should be sent in the correct order, and timestamps MUST be non-decreasing.
! A sysex message will generally fill many PmEvent structures. On output to a PortMidiStream with non-zero latency, the first timestamp ! on sysex message data will determine the time to begin sending the ! message. PortMidi implementations may ignore timestamps for the ! remainder of the sysex message. ! ! On input, the timestamp ideally denotes the arrival time of the ! status byte of the message. The first timestamp on sysex message ! data will be valid. Subsequent timestamps may denote ! when message bytes were actually received, or they may be simply copies of the first timestamp.
! Timestamps for nested messages: If a real-time message arrives in ! the middle of some other message, it is enqueued immediately with ! the timestamp corresponding to its arrival time. The interrupted ! non-real-time message or 4-byte packet of sysex data will be enqueued later. The timestamp of interrupted data will be equal to that of the interrupting real-time message to insure that timestamps are *************** *** 628,644 **** /* Pm_Read() retrieves midi data into a buffer, and returns the number ! of events read. Result is a non-negative number unless an error occurs, in which case a PmError value will be returned.
Buffer Overflow
! The problem: if an input overflow occurs, data will be lost, ultimately ! because there is no flow control all the way back to the data source. ! When data is lost, the receiver should be notified and some sort of ! graceful recovery should take place, e.g. you shouldn't resume receiving in the middle of a long sysex message.
! With a lock-free fifo, which is pretty much what we're stuck with to ! enable portability to the Mac, it's tricky for the producer and consumer to synchronously reset the buffer and resume normal operation.
--- 542,558 ---- /* Pm_Read() retrieves midi data into a buffer, and returns the number ! of events read. Result is a non-negative number unless an error occurs, in which case a PmError value will be returned.
Buffer Overflow
! The problem: if an input overflow occurs, data will be lost, ultimately ! because there is no flow control all the way back to the data source. ! When data is lost, the receiver should be notified and some sort of ! graceful recovery should take place, e.g. you shouldn't resume receiving in the middle of a long sysex message.
! With a lock-free fifo, which is pretty much what we're stuck with to ! enable portability to the Mac, it's tricky for the producer and consumer to synchronously reset the buffer and resume normal operation.
*************** *** 653,670 ****
/* ! Pm_Poll() tests whether input is available, returning TRUE, FALSE, or an error value. */ PmError Pm_Poll( PortMidiStream *stream);
! /* Pm_Write() writes midi data from a buffer. This may contain: ! - short messages ! or - sysex messages that are converted into a sequence of PmEvent structures, e.g. sending data from a file or forwarding them from midi input.
! Use Pm_WriteSysEx() to write a sysex message stored as a contiguous array of bytes.
--- 567,584 ----
/* ! Pm_Poll() tests whether input is available, returning TRUE, FALSE, or an error value. */ PmError Pm_Poll( PortMidiStream *stream);
! /* Pm_Write() writes midi data from a buffer. This may contain: ! - short messages ! or - sysex messages that are converted into a sequence of PmEvent structures, e.g. sending data from a file or forwarding them from midi input.
! Use Pm_WriteSysEx() to write a sysex message stored as a contiguous array of bytes.
*************** *** 675,681 **** /* Pm_WriteShort() writes a timestamped non-system-exclusive midi message. ! Messages are delivered in order as received, and timestamps must be ! non-decreasing. (But timestamps are ignored if the stream was opened ! with latency = 0.) */ PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg); --- 589,595 ---- /* Pm_WriteShort() writes a timestamped non-system-exclusive midi message. ! Messages are delivered in order as received, and timestamps must be ! non-decreasing. (But timestamps are ignored if the stream was opened ! with latency = 0.) */ PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg);
Index: pmutil.h =================================================================== RCS file: /cvsroot/pure-data/pd/portmidi/pm_common/pmutil.h,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** pmutil.h 15 Dec 2005 00:56:57 -0000 1.1 --- pmutil.h 16 Jan 2008 21:54:10 -0000 1.2 *************** *** 3,6 **** --- 3,10 ---- */
+ #ifdef __cplusplus + extern "C" { + #endif /* __cplusplus */ + typedef void PmQueue;
*************** *** 11,14 **** --- 15,45 ---- fixed sized messages. Returns NULL if memory cannot be allocated.
+ This queue implementation uses the "light pipe" algorithm which + operates correctly even with multi-processors and out-of-order + memory writes. (see Alexander Dokumentov, "Lock-free Interprocess + Communication," Dr. Dobbs Portal, http://www.ddj.com/, + articleID=189401457, June 15, 2006. This algorithm requires + that messages be translated to a form where no words contain + zeros. Each word becomes its own "data valid" tag. Because of + this translation, we cannot return a pointer to data still in + the queue when the "peek" method is called. Instead, a buffer + is preallocated so that data can be copied there. Pm_QueuePeek() + dequeues a message into this buffer and returns a pointer to + it. A subsequent Pm_Dequeue() will copy from this buffer. + + This implementation does not try to keep reader/writer data in + separate cache lines or prevent thrashing on cache lines. + However, this algorithm differs by doing inserts/removals in + units of messages rather than units of machine words. Some + performance improvement might be obtained by not clearing data + immediately after a read, but instead by waiting for the end + of the cache line, especially if messages are smaller than + cache lines. See the Dokumentov article for explanation. + + The algorithm is extended to handle "overflow" reporting. To report + an overflow, the sender writes the current tail position to a field. + The receiver must acknowlege receipt by zeroing the field. The sender + will not send more until the field is zeroed. + Pm_QueueDestroy() destroys the queue and frees its storage. */ *************** *** 20,27 **** Pm_Dequeue() removes one item from the queue, copying it into msg. Returns 1 if successful, and 0 if the queue is empty. ! Returns pmBufferOverflow, clears the overflow flag, and does not ! return a data item if the overflow flag is set. (This protocol ! ensures that the reader will be notified when data is lost due ! to overflow.) */ PmError Pm_Dequeue(PmQueue *queue, void *msg); --- 51,59 ---- Pm_Dequeue() removes one item from the queue, copying it into msg. Returns 1 if successful, and 0 if the queue is empty. ! Returns pmBufferOverflow if what would have been the next thing ! in the queue was dropped due to overflow. (So when overflow occurs, ! the receiver can receive a queue full of messages before getting the ! overflow report. This protocol ensures that the reader will be ! notified when data is lost due to overflow. */ PmError Pm_Dequeue(PmQueue *queue, void *msg); *************** *** 41,45 ****
Either condition may change immediately because a parallel ! enqueue or dequeue operation could be in progress. */ int Pm_QueueFull(PmQueue *queue); --- 73,82 ----
Either condition may change immediately because a parallel ! enqueue or dequeue operation could be in progress. Furthermore, ! Pm_QueueEmpty() is optimistic: it may say false, when due to ! out-of-order writes, the full message has not arrived. Therefore, ! Pm_Dequeue() could still return 0 after Pm_QueueEmpty() returns ! false. On the other hand, Pm_QueueFull() is pessimistic: if it ! returns false, then Pm_Enqueue() is guaranteed to succeed. */ int Pm_QueueFull(PmQueue *queue); *************** *** 50,56 **** Pm_QueuePeek() returns a pointer to the item at the head of the queue, or NULL if the queue is empty. The item is not removed from the queue. ! If queue is in an overflow state, a valid pointer is returned and the ! queue remains in the overflow state. */ void *Pm_QueuePeek(PmQueue *queue);
--- 87,124 ---- Pm_QueuePeek() returns a pointer to the item at the head of the queue, or NULL if the queue is empty. The item is not removed from the queue. ! Pm_QueuePeek() will not indicate when an overflow occurs. If you want ! to get and check pmBufferOverflow messages, use the return value of ! Pm_QueuePeek() *only* as an indication that you should call ! Pm_Dequeue(). At the point where a direct call to Pm_Dequeue() would ! return pmBufferOverflow, Pm_QueuePeek() will return NULL but internally ! clear the pmBufferOverflow flag, enabling Pm_Enqueue() to resume ! enqueuing messages. A subsequent call to Pm_QueuePeek() ! will return a pointer to the first message *after* the overflow. ! Using this as an indication to call Pm_Dequeue(), the first call ! to Pm_Dequeue() will return pmBufferOverflow. The second call will ! return success, copying the same message pointed to by the previous ! Pm_QueuePeek(). ! ! When to use Pm_QueuePeek(): (1) when you need to look at the message ! data to decide who should be called to receive it. (2) when you need ! to know a message is ready but cannot accept the message. ! ! Note that Pm_QueuePeek() is not a fast check, so if possible, you ! might as well just call Pm_Dequeue() and accept the data if it is there. */ void *Pm_QueuePeek(PmQueue *queue);
+ /* + Pm_SetOverflow() allows the writer (enqueuer) to signal an overflow + condition to the reader (dequeuer). E.g. when transfering data from + the OS to an application, if the OS indicates a buffer overrun, + Pm_SetOverflow() can be used to insure that the reader receives a + pmBufferOverflow result from Pm_Dequeue(). Returns pmBadPtr if queue + is NULL, returns pmBufferOverflow if buffer is already in an overflow + state, returns pmNoError if successfully set overflow state. + */ + PmError Pm_SetOverflow(PmQueue *queue); + + #ifdef __cplusplus + } + #endif /* __cplusplus */