lms-video/pd-0.44-2/portmidi/pm_common/pmutil.c
Santi Noreña e85d191b46 - Reestructuración de ficheros y directorios general
- merge v0.01 --> Añadido fileselector
- Añadidas fuentes de Gem y Pure Data
- pix2jpg incluído en Gem. Archivos de construcción de Gem modificados.
- Añadido fichero ompiling.txt con instrucciones de compilación
2013-02-04 18:00:17 +01:00

311 lines
9 KiB
C
Executable file

/* pmutil.c -- some helpful utilities for building midi
applications that use PortMidi
*/
#include "stdlib.h"
#include "assert.h"
#include "memory.h"
#include "portmidi.h"
#include "pmutil.h"
#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;
}
PmError Pm_QueueDestroy(PmQueue *q)
{
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;
}
PmError Pm_Dequeue(PmQueue *q, void *msg)
{
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;
queue->head = head;
return 1; /* success */
}
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;
}