aboutsummaryrefslogtreecommitdiff
path: root/portmidi/pm_common/pmutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'portmidi/pm_common/pmutil.c')
-rwxr-xr-xportmidi/pm_common/pmutil.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/portmidi/pm_common/pmutil.c b/portmidi/pm_common/pmutil.c
new file mode 100755
index 0000000..a70fe2f
--- /dev/null
+++ b/portmidi/pm_common/pmutil.c
@@ -0,0 +1,284 @@
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
20typedef 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
33PMEXPORT 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
70PMEXPORT 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
85PMEXPORT 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
164PMEXPORT 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
179PMEXPORT 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
221PMEXPORT 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
229PMEXPORT 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
248PMEXPORT 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