logo

overlay

My own overlay for experimentations, use with caution, no support is provided git clone https://hacktivis.me/git/overlay.git

portmidi-217-pmsndio.c (9481B)


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