aboutsummaryrefslogtreecommitdiff
path: root/portmidi/pm_sndio
diff options
context:
space:
mode:
Diffstat (limited to 'portmidi/pm_sndio')
-rw-r--r--portmidi/pm_sndio/pmsndio.c365
-rw-r--r--portmidi/pm_sndio/pmsndio.h5
2 files changed, 370 insertions, 0 deletions
diff --git a/portmidi/pm_sndio/pmsndio.c b/portmidi/pm_sndio/pmsndio.c
new file mode 100644
index 0000000..0c1ea11
--- /dev/null
+++ b/portmidi/pm_sndio/pmsndio.c
@@ -0,0 +1,365 @@
1/* pmsndio.c -- PortMidi os-dependent code */
2
3#include <stdlib.h>
4#include <stdio.h>
5#include <sndio.h>
6#include <string.h>
7#include <poll.h>
8#include <errno.h>
9#include <pthread.h>
10#include "portmidi.h"
11#include "pmutil.h"
12#include "pminternal.h"
13#include "porttime.h"
14
15#define NDEVS 9
16#define SYSEX_MAXLEN 1024
17
18#define SYSEX_START 0xf0
19#define SYSEX_END 0xf7
20
21extern pm_fns_node pm_sndio_in_dictionary;
22extern pm_fns_node pm_sndio_out_dictionary;
23
24/* length of voice and common messages (status byte included) */
25unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
26unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
27
28struct mio_dev {
29 char name[16];
30 struct mio_hdl *hdl;
31 int mode;
32 char errmsg[PM_HOST_ERROR_MSG_LEN];
33 pthread_t thread;
34} devs[NDEVS];
35
36static void set_mode(struct mio_dev *, unsigned int);
37
38void pm_init()
39{
40 int i, j, k = 0;
41 char devices[][16] = {"midithru", "rmidi", "midi", "snd"};
42
43 /* default */
44 strcpy(devs[0].name, MIO_PORTANY);
45 pm_add_device("SNDIO", devs[k].name, TRUE, FALSE, (void *) &devs[k],
46 &pm_sndio_in_dictionary);
47 pm_add_device("SNDIO", devs[k].name, FALSE, FALSE, (void *) &devs[k],
48 &pm_sndio_out_dictionary);
49 k++;
50
51 for (i = 0; i < 4; i++) {
52 for (j = 0; j < 2; j++) {
53 sprintf(devs[k].name, "%s/%d", devices[i], j);
54 pm_add_device("SNDIO", devs[k].name, TRUE, FALSE, (void *) &devs[k],
55 &pm_sndio_in_dictionary);
56 pm_add_device("SNDIO", devs[k].name, FALSE, FALSE, (void *) &devs[k],
57 &pm_sndio_out_dictionary);
58 k++;
59 }
60 }
61
62 // this is set when we return to Pm_Initialize, but we need it
63 // now in order to (successfully) call Pm_CountDevices()
64 pm_initialized = TRUE;
65 pm_default_input_device_id = 0;
66 pm_default_output_device_id = 1;
67}
68
69void pm_term(void)
70{
71 int i;
72 for(i = 0; i < NDEVS; i++) {
73 if (devs[i].mode != 0) {
74 set_mode(&devs[i], 0);
75 if (devs[i].thread) {
76 pthread_join(devs[i].thread, NULL);
77 devs[i].thread = NULL;
78 }
79 }
80 }
81}
82
83PmDeviceID Pm_GetDefaultInputDeviceID() {
84 Pm_Initialize();
85 return pm_default_input_device_id;
86}
87
88PmDeviceID Pm_GetDefaultOutputDeviceID() {
89 Pm_Initialize();
90 return pm_default_output_device_id;
91}
92
93void *pm_alloc(size_t s) { return malloc(s); }
94
95void pm_free(void *ptr) { free(ptr); }
96
97/* midi_message_length -- how many bytes in a message? */
98static int midi_message_length(PmMessage message)
99{
100 unsigned char st = message & 0xff;
101 if (st >= 0xf8)
102 return 1;
103 else if (st >= 0xf0)
104 return common_len[st & 7];
105 else if (st >= 0x80)
106 return voice_len[(st >> 4) & 7];
107 else
108 return 0;
109}
110
111void* input_thread(void *param)
112{
113 PmInternal *midi = (PmInternal*)param;
114 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
115 struct pollfd pfd[1];
116 nfds_t nfds;
117 unsigned char st = 0, c = 0;
118 int rc, revents, idx = 0, len = 0;
119 size_t todo = 0;
120 unsigned char buf[0x200], *p;
121 PmEvent pm_ev, pm_ev_rt;
122 unsigned char sysex_data[SYSEX_MAXLEN];
123
124 while(dev->mode & MIO_IN) {
125 if (todo == 0) {
126 nfds = mio_pollfd(dev->hdl, pfd, POLLIN);
127 rc = poll(pfd, nfds, 100);
128 if (rc < 0) {
129 if (errno == EINTR)
130 continue;
131 break;
132 }
133 revents = mio_revents(dev->hdl, pfd);
134 if (!(revents & POLLIN))
135 continue;
136
137 todo = mio_read(dev->hdl, buf, sizeof(buf));
138 if (todo == 0)
139 continue;
140 p = buf;
141 }
142 c = *p++;
143 todo--;
144
145 if (c >= 0xf8) {
146 pm_ev_rt.message = c;
147 pm_ev_rt.timestamp = Pt_Time();
148 pm_read_short(midi, &pm_ev_rt);
149 } else if (c == SYSEX_END) {
150 /* note: PortMidi is designed to avoid the need for SYSEX_MAXLEN.
151 With the new implementation of pm_read_bytes, it would be
152 better to simply call pm_read_bytes() and let it parse buf,
153 which can contain any number of whole or partial messages with
154 interleaved realtime messages. I did not change the code because
155 I cannot test it. -RBD */
156 if (st == SYSEX_START) {
157 sysex_data[idx++] = c;
158 pm_read_bytes(midi, sysex_data, idx, Pt_Time());
159 }
160 st = 0;
161 idx = 0;
162 } else if (c == SYSEX_START) {
163 st = c;
164 idx = 0;
165 sysex_data[idx++] = c;
166 } else if (c >= 0xf0) {
167 pm_ev.message = c;
168 len = common_len[c & 7];
169 st = c;
170 idx = 1;
171 } else if (c >= 0x80) {
172 pm_ev.message = c;
173 len = voice_len[(c >> 4) & 7];
174 st = c;
175 idx = 1;
176 } else if (st == SYSEX_START) {
177 if (idx == SYSEX_MAXLEN) {
178 fprintf(stderr, "the message is too long\n");
179 idx = st = 0;
180 } else {
181 sysex_data[idx++] = c;
182 }
183 } else if (st) {
184 if (idx == 0 && st != SYSEX_START)
185 pm_ev.message |= (c << (8 * idx++));
186 pm_ev.message |= (c << (8 * idx++));
187 if (idx == len) {
188 pm_read_short(midi, &pm_ev);
189 if (st >= 0xf0)
190 st = 0;
191 idx = 0;
192 }
193 }
194 }
195
196 pthread_exit(NULL);
197 return NULL;
198}
199
200static void set_mode(struct mio_dev *dev, unsigned int mode) {
201 if (dev->mode != 0)
202 mio_close(dev->hdl);
203 dev->mode = 0;
204 if (mode != 0)
205 dev->hdl = mio_open(dev->name, mode, 0);
206 if (dev->hdl)
207 dev->mode = mode;
208}
209
210static PmError sndio_out_open(PmInternal *midi, void *driverInfo)
211{
212 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
213
214 if (dev->mode & MIO_OUT)
215 return pmNoError;
216
217 set_mode(dev, dev->mode | MIO_OUT);
218 if (!(dev->mode & MIO_OUT)) {
219 snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN,
220 "mio_open (output) failed: %s\n", dev->name);
221 return pmHostError;
222 }
223
224 return pmNoError;
225}
226
227static PmError sndio_in_open(PmInternal *midi, void *driverInfo)
228{
229 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
230
231 if (dev->mode & MIO_IN)
232 return pmNoError;
233
234 set_mode(dev, dev->mode | MIO_IN);
235 if (!(dev->mode & MIO_IN)) {
236 snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN,
237 "mio_open (input) failed: %s\n", dev->name);
238 return pmHostError;
239 }
240 pthread_attr_t attr;
241 pthread_attr_init(&attr);
242 pthread_create(&dev->thread, &attr, input_thread, ( void* )midi);
243 return pmNoError;
244}
245
246static PmError sndio_out_close(PmInternal *midi)
247{
248 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
249
250 if (dev->mode & MIO_OUT)
251 set_mode(dev, dev->mode & ~MIO_OUT);
252 return pmNoError;
253}
254
255static PmError sndio_in_close(PmInternal *midi)
256{
257 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
258
259 if (dev->mode & MIO_IN) {
260 set_mode(dev, dev->mode & ~MIO_IN);
261 pthread_join(dev->thread, NULL);
262 dev->thread = NULL;
263 }
264 return pmNoError;
265}
266
267static PmError sndio_abort(PmInternal *midi)
268{
269 return pmNoError;
270}
271
272static PmTimestamp sndio_synchronize(PmInternal *midi)
273{
274 return 0;
275}
276
277static PmError do_write(struct mio_dev *dev, const void *addr, size_t nbytes)
278{
279 size_t w = mio_write(dev->hdl, addr, nbytes);
280
281 if (w != nbytes) {
282 snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN,
283 "mio_write failed, bytes written:%zu\n", w);
284 return pmHostError;
285 }
286 return pmNoError;
287}
288
289static PmError sndio_write_byte(PmInternal *midi, unsigned char byte,
290 PmTimestamp timestamp)
291{
292 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
293
294 return do_write(dev, &byte, 1);
295}
296
297static PmError sndio_write_short(PmInternal *midi, PmEvent *event)
298{
299 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
300 int nbytes = midi_message_length(event->message);
301
302 if (midi->latency > 0) {
303 /* XXX the event should be queued for later playback */
304 return do_write(dev, &event->message, nbytes);
305 } else {
306 return do_write(dev, &event->message, nbytes);
307 }
308 return pmNoError;
309}
310
311static PmError sndio_write_flush(PmInternal *midi, PmTimestamp timestamp)
312{
313 return pmNoError;
314}
315
316PmError sndio_sysex(PmInternal *midi, PmTimestamp timestamp)
317{
318 return pmNoError;
319}
320
321static unsigned int sndio_has_host_error(PmInternal *midi)
322{
323 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
324
325 return (dev->errmsg[0] != '\0');
326}
327
328static void sndio_get_host_error(PmInternal *midi, char *msg, unsigned int len)
329{
330 struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor;
331
332 strlcpy(msg, dev->errmsg, len);
333 dev->errmsg[0] = '\0';
334}
335
336pm_fns_node pm_sndio_in_dictionary = {
337 none_write_short,
338 none_sysex,
339 none_sysex,
340 none_write_byte,
341 none_write_short,
342 none_write_flush,
343 sndio_synchronize,
344 sndio_in_open,
345 sndio_abort,
346 sndio_in_close,
347 success_poll,
348 sndio_has_host_error,
349};
350
351pm_fns_node pm_sndio_out_dictionary = {
352 sndio_write_short,
353 sndio_sysex,
354 sndio_sysex,
355 sndio_write_byte,
356 sndio_write_short,
357 sndio_write_flush,
358 sndio_synchronize,
359 sndio_out_open,
360 sndio_abort,
361 sndio_out_close,
362 none_poll,
363 sndio_has_host_error,
364};
365
diff --git a/portmidi/pm_sndio/pmsndio.h b/portmidi/pm_sndio/pmsndio.h
new file mode 100644
index 0000000..4096d9b
--- /dev/null
+++ b/portmidi/pm_sndio/pmsndio.h
@@ -0,0 +1,5 @@
1/* pmsndio.h */
2
3extern PmDeviceID pm_default_input_device_id;
4extern PmDeviceID pm_default_output_device_id;
5