globally remove all trailing whitespace from ardour code base.
[ardour.git] / libs / backends / wavesaudio / portmidi / src / pm_common / pmutil.c
1 /* pmutil.c -- some helpful utilities for building midi
2                applications that use PortMidi
3  */
4 #include <stdlib.h>
5 #include <assert.h>
6 #include <string.h>
7 #include "portmidi.h"
8 #include "pmutil.h"
9 #include "pminternal.h"
10
11 #ifdef WIN32
12 #define bzero(addr, siz) memset(addr, 0, siz)
13 #endif
14
15 // #define QUEUE_DEBUG 1
16 #ifdef QUEUE_DEBUG
17 #include "stdio.h"
18 #endif
19
20 typedef struct {
21     long head;
22     long tail;
23     long len;
24     long overflow;
25     int32_t msg_size; /* number of int32_t in a message including extra word */
26     int32_t peek_overflow;
27     int32_t *buffer;
28     int32_t *peek;
29     int32_t peek_flag;
30 } PmQueueRep;
31
32
33 PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg)
34 {
35     int32_t int32s_per_msg =
36             (int32_t) (((bytes_per_msg + sizeof(int32_t) - 1) &
37                        ~(sizeof(int32_t) - 1)) / sizeof(int32_t));
38     PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep));
39     if (!queue) /* memory allocation failed */
40         return NULL;
41
42     /* need extra word per message for non-zero encoding */
43     queue->len = num_msgs * (int32s_per_msg + 1);
44     queue->buffer = (int32_t *) pm_alloc(queue->len * sizeof(int32_t));
45     bzero(queue->buffer, queue->len * sizeof(int32_t));
46     if (!queue->buffer) {
47         pm_free(queue);
48         return NULL;
49     } else { /* allocate the "peek" buffer */
50         queue->peek = (int32_t *) pm_alloc(int32s_per_msg * sizeof(int32_t));
51         if (!queue->peek) {
52             /* free everything allocated so far and return */
53             pm_free(queue->buffer);
54             pm_free(queue);
55             return NULL;
56         }
57     }
58     bzero(queue->buffer, queue->len * sizeof(int32_t));
59     queue->head = 0;
60     queue->tail = 0;
61     /* msg_size is in words */
62     queue->msg_size = int32s_per_msg + 1; /* note extra word is counted */
63     queue->overflow = FALSE;
64     queue->peek_overflow = FALSE;
65     queue->peek_flag = FALSE;
66     return queue;
67 }
68
69
70 PMEXPORT PmError Pm_QueueDestroy(PmQueue *q)
71 {
72     PmQueueRep *queue = (PmQueueRep *) q;
73
74     /* arg checking */
75     if (!queue || !queue->buffer || !queue->peek)
76                 return pmBadPtr;
77
78     pm_free(queue->peek);
79     pm_free(queue->buffer);
80     pm_free(queue);
81     return pmNoError;
82 }
83
84
85 PMEXPORT PmError Pm_Dequeue(PmQueue *q, void *msg)
86 {
87     long head;
88     PmQueueRep *queue = (PmQueueRep *) q;
89     int i;
90     int32_t *msg_as_int32 = (int32_t *) msg;
91
92     /* arg checking */
93     if (!queue)
94         return pmBadPtr;
95     /* a previous peek operation encountered an overflow, but the overflow
96      * has not yet been reported to client, so do it now. No message is
97      * returned, but on the next call, we will return the peek buffer.
98      */
99     if (queue->peek_overflow) {
100         queue->peek_overflow = FALSE;
101         return pmBufferOverflow;
102     }
103     if (queue->peek_flag) {
104         memcpy(msg, queue->peek, (queue->msg_size - 1) * sizeof(int32_t));
105         queue->peek_flag = FALSE;
106         return pmGotData;
107     }
108
109     head = queue->head;
110     /* if writer overflows, it writes queue->overflow = tail+1 so that
111      * when the reader gets to that position in the buffer, it can
112      * return the overflow condition to the reader. The problem is that
113      * at overflow, things have wrapped around, so tail == head, and the
114      * reader will detect overflow immediately instead of waiting until
115      * it reads everything in the buffer, wrapping around again to the
116      * point where tail == head. So the condition also checks that
117      * queue->buffer[head] is zero -- if so, then the buffer is now
118      * empty, and we're at the point in the msg stream where overflow
119      * occurred. It's time to signal overflow to the reader. If
120      * queue->buffer[head] is non-zero, there's a message there and we
121      * should read all the way around the buffer before signalling overflow.
122      * There is a write-order dependency here, but to fail, the overflow
123      * field would have to be written while an entire buffer full of
124      * writes are still pending. I'm assuming out-of-order writes are
125      * possible, but not that many.
126      */
127     if (queue->overflow == head + 1 && !queue->buffer[head]) {
128         queue->overflow = 0; /* non-overflow condition */
129         return pmBufferOverflow;
130     }
131
132     /* test to see if there is data in the queue -- test from back
133      * to front so if writer is simultaneously writing, we don't
134      * waste time discovering the write is not finished
135      */
136     for (i = queue->msg_size - 1; i >= 0; i--) {
137         if (!queue->buffer[head + i]) {
138             return pmNoData;
139         }
140     }
141     memcpy(msg, (char *) &queue->buffer[head + 1],
142            sizeof(int32_t) * (queue->msg_size - 1));
143     /* fix up zeros */
144     i = queue->buffer[head];
145     while (i < queue->msg_size) {
146         int32_t j;
147         i--; /* msg does not have extra word so shift down */
148         j = msg_as_int32[i];
149         msg_as_int32[i] = 0;
150         i = j;
151     }
152     /* signal that data has been removed by zeroing: */
153     bzero((char *) &queue->buffer[head], sizeof(int32_t) * queue->msg_size);
154
155     /* update head */
156     head += queue->msg_size;
157     if (head == queue->len) head = 0;
158     queue->head = head;
159     return pmGotData; /* success */
160 }
161
162
163
164 PMEXPORT PmError Pm_SetOverflow(PmQueue *q)
165 {
166     PmQueueRep *queue = (PmQueueRep *) q;
167     long tail;
168     /* arg checking */
169     if (!queue)
170         return pmBadPtr;
171     /* no more enqueue until receiver acknowledges overflow */
172     if (queue->overflow) return pmBufferOverflow;
173     tail = queue->tail;
174     queue->overflow = tail + 1;
175     return pmBufferOverflow;
176 }
177
178
179 PMEXPORT PmError Pm_Enqueue(PmQueue *q, void *msg)
180 {
181     PmQueueRep *queue = (PmQueueRep *) q;
182     long tail;
183     int i;
184     int32_t *src = (int32_t *) msg;
185     int32_t *ptr;
186     int32_t *dest;
187     int rslt;
188     if (!queue)
189         return pmBadPtr;
190     /* no more enqueue until receiver acknowledges overflow */
191     if (queue->overflow) return pmBufferOverflow;
192     rslt = Pm_QueueFull(q);
193     /* already checked above: if (rslt == pmBadPtr) return rslt; */
194     tail = queue->tail;
195     if (rslt) {
196         queue->overflow = tail + 1;
197         return pmBufferOverflow;
198     }
199
200     /* queue is has room for message, and overflow flag is cleared */
201     ptr = &queue->buffer[tail];
202     dest = ptr + 1;
203     for (i = 1; i < queue->msg_size; i++) {
204         int32_t j = src[i - 1];
205         if (!j) {
206             *ptr = i;
207             ptr = dest;
208         } else {
209             *dest = j;
210         }
211         dest++;
212     }
213     *ptr = i;
214     tail += queue->msg_size;
215     if (tail == queue->len) tail = 0;
216     queue->tail = tail;
217     return pmNoError;
218 }
219
220
221 PMEXPORT int Pm_QueueEmpty(PmQueue *q)
222 {
223     PmQueueRep *queue = (PmQueueRep *) q;
224     return (!queue) ||  /* null pointer -> return "empty" */
225            (queue->buffer[queue->head] == 0 && !queue->peek_flag);
226 }
227
228
229 PMEXPORT int Pm_QueueFull(PmQueue *q)
230 {
231     long tail;
232     int i;
233     PmQueueRep *queue = (PmQueueRep *) q;
234     /* arg checking */
235     if (!queue)
236         return pmBadPtr;
237     tail = queue->tail;
238     /* test to see if there is space in the queue */
239     for (i = 0; i < queue->msg_size; i++) {
240         if (queue->buffer[tail + i]) {
241             return TRUE;
242         }
243     }
244     return FALSE;
245 }
246
247
248 PMEXPORT void *Pm_QueuePeek(PmQueue *q)
249 {
250     PmError rslt;
251     int32_t temp;
252     PmQueueRep *queue = (PmQueueRep *) q;
253     /* arg checking */
254     if (!queue)
255         return NULL;
256
257     if (queue->peek_flag) {
258         return queue->peek;
259     }
260     /* this is ugly: if peek_overflow is set, then Pm_Dequeue()
261      * returns immediately with pmBufferOverflow, but here, we
262      * want Pm_Dequeue() to really check for data. If data is
263      * there, we can return it
264      */
265     temp = queue->peek_overflow;
266     queue->peek_overflow = FALSE;
267     rslt = Pm_Dequeue(q, queue->peek);
268     queue->peek_overflow = temp;
269
270     if (rslt == 1) {
271         queue->peek_flag = TRUE;
272         return queue->peek;
273     } else if (rslt == pmBufferOverflow) {
274         /* when overflow is indicated, the queue is empty and the
275          * first message that was dropped by Enqueue (signalling
276          * pmBufferOverflow to its caller) would have been the next
277          * message in the queue. Pm_QueuePeek will return NULL, but
278          * remember that an overflow occurred. (see Pm_Dequeue)
279          */
280         queue->peek_overflow = TRUE;
281     }
282     return NULL;
283 }
284