[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
- Previous message: [PD-cvs] pd/portmidi/pm_win README_WIN.txt, 1.1, 1.2 copy-dll.bat, 1.1, 1.2 pm_dll.dsp, 1.1, 1.2 pmwin.c, 1.1, 1.2 pmwinmm.c, 1.1, 1.2
- Next message: [PD-cvs] pd/src g_editor.c, 1.21, 1.22 m_class.c, 1.8, 1.9 m_pd.h, 1.21, 1.22 s_file.c, 1.11, 1.12 s_inter.c, 1.21, 1.22 s_main.c, 1.32, 1.33 s_path.c, 1.14, 1.15 u_main.tk, 1.30, 1.31 x_connective.c, 1.10, 1.11
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
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 */
- Previous message: [PD-cvs] pd/portmidi/pm_win README_WIN.txt, 1.1, 1.2 copy-dll.bat, 1.1, 1.2 pm_dll.dsp, 1.1, 1.2 pmwin.c, 1.1, 1.2 pmwinmm.c, 1.1, 1.2
- Next message: [PD-cvs] pd/src g_editor.c, 1.21, 1.22 m_class.c, 1.8, 1.9 m_pd.h, 1.21, 1.22 s_file.c, 1.11, 1.12 s_inter.c, 1.21, 1.22 s_main.c, 1.32, 1.33 s_path.c, 1.14, 1.15 u_main.tk, 1.30, 1.31 x_connective.c, 1.10, 1.11
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the Pd-cvs
mailing list