[PD-cvs] pd/portmidi/pm_common pminternal.h, 1.1, 1.2 pmutil.c, 1.1, 1.2 pmutil.h, 1.1, 1.2 portmidi.c, 1.1, 1.2 portmidi.h, 1.1, 1.2

Miller Puckette millerpuckette at users.sourceforge.net
Wed Jan 16 22:54:12 CET 2008


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 */





More information about the Pd-cvs mailing list