jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
7.6KB

  1. /*
  2. Copyright (C) 2011 Devin Anderson
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. */
  15. #include <cassert>
  16. #include <stdexcept>
  17. #include <string>
  18. #include "JackALSARawMidiPort.h"
  19. #include "JackALSARawMidiUtil.h"
  20. #include "JackError.h"
  21. using Jack::JackALSARawMidiPort;
  22. JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
  23. size_t index, unsigned short io_mask)
  24. {
  25. int card = snd_rawmidi_info_get_card(info);
  26. unsigned int device = snd_rawmidi_info_get_device(info);
  27. unsigned int subdevice = snd_rawmidi_info_get_subdevice(info);
  28. char device_id[32];
  29. snprintf(device_id, sizeof(device_id), "hw:%d,%d,%d", card, device,
  30. subdevice);
  31. const char *alias_suffix;
  32. const char *error_message;
  33. snd_rawmidi_t **in;
  34. const char *name_prefix;
  35. snd_rawmidi_t **out;
  36. if (snd_rawmidi_info_get_stream(info) == SND_RAWMIDI_STREAM_OUTPUT) {
  37. alias_suffix = "out";
  38. in = 0;
  39. name_prefix = "system:midi_playback_";
  40. out = &rawmidi;
  41. } else {
  42. alias_suffix = "in";
  43. in = &rawmidi;
  44. name_prefix = "system:midi_capture_";
  45. out = 0;
  46. }
  47. const char *func;
  48. int code = snd_rawmidi_open(in, out, device_id, SND_RAWMIDI_NONBLOCK);
  49. if (code) {
  50. error_message = snd_strerror(code);
  51. func = "snd_rawmidi_open";
  52. goto handle_error;
  53. }
  54. snd_rawmidi_params_t *params;
  55. code = snd_rawmidi_params_malloc(&params);
  56. if (code) {
  57. error_message = snd_strerror(code);
  58. func = "snd_rawmidi_params_malloc";
  59. goto close;
  60. }
  61. code = snd_rawmidi_params_current(rawmidi, params);
  62. if (code) {
  63. error_message = snd_strerror(code);
  64. func = "snd_rawmidi_params_current";
  65. goto free_params;
  66. }
  67. code = snd_rawmidi_params_set_avail_min(rawmidi, params, 1);
  68. if (code) {
  69. error_message = snd_strerror(code);
  70. func = "snd_rawmidi_params_set_avail_min";
  71. goto free_params;
  72. }
  73. // Minimum buffer size allowed by ALSA
  74. code = snd_rawmidi_params_set_buffer_size(rawmidi, params, 32);
  75. if (code) {
  76. error_message = snd_strerror(code);
  77. func = "snd_rawmidi_params_set_buffer_size";
  78. goto free_params;
  79. }
  80. code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1);
  81. if (code) {
  82. error_message = snd_strerror(code);
  83. func = "snd_rawmidi_params_set_no_active_sensing";
  84. goto free_params;
  85. }
  86. code = snd_rawmidi_params(rawmidi, params);
  87. if (code) {
  88. error_message = snd_strerror(code);
  89. func = "snd_rawmidi_params";
  90. goto free_params;
  91. }
  92. snd_rawmidi_params_free(params);
  93. alsa_poll_fd_count = snd_rawmidi_poll_descriptors_count(rawmidi);
  94. if (! alsa_poll_fd_count) {
  95. error_message = "returned '0' count for poll descriptors";
  96. func = "snd_rawmidi_poll_descriptors_count";
  97. goto close;
  98. }
  99. try {
  100. CreateNonBlockingPipe(fds);
  101. } catch (std::exception e) {
  102. error_message = e.what();
  103. func = "CreateNonBlockingPipe";
  104. goto close;
  105. }
  106. snprintf(alias, sizeof(alias), "system:%d-%d %s %d %s", card + 1,
  107. device + 1, snd_rawmidi_info_get_name(info), subdevice + 1,
  108. alias_suffix);
  109. snprintf(name, sizeof(name), "%s%zu", name_prefix, index + 1);
  110. this->io_mask = io_mask;
  111. return;
  112. free_params:
  113. snd_rawmidi_params_free(params);
  114. close:
  115. snd_rawmidi_close(rawmidi);
  116. handle_error:
  117. throw std::runtime_error(std::string(func) + ": " + error_message);
  118. }
  119. JackALSARawMidiPort::~JackALSARawMidiPort()
  120. {
  121. DestroyNonBlockingPipe(fds);
  122. if (rawmidi) {
  123. int code = snd_rawmidi_close(rawmidi);
  124. if (code) {
  125. jack_error("JackALSARawMidiPort::~JackALSARawMidiPort - "
  126. "snd_rawmidi_close: %s", snd_strerror(code));
  127. }
  128. rawmidi = 0;
  129. }
  130. }
  131. const char *
  132. JackALSARawMidiPort::GetAlias()
  133. {
  134. return alias;
  135. }
  136. int
  137. JackALSARawMidiPort::GetIOPollEvent()
  138. {
  139. unsigned short events;
  140. int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds,
  141. alsa_poll_fd_count,
  142. &events);
  143. if (code) {
  144. jack_error("JackALSARawMidiPort::GetIOPollEvents - "
  145. "snd_rawmidi_poll_descriptors_revents: %s",
  146. snd_strerror(code));
  147. return -1;
  148. }
  149. if (events & POLLNVAL) {
  150. jack_error("JackALSARawMidiPort::GetIOPollEvents - the file "
  151. "descriptor is invalid.");
  152. return -1;
  153. }
  154. if (events & POLLERR) {
  155. jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has "
  156. "occurred on the device or stream.");
  157. return -1;
  158. }
  159. return (events & io_mask) ? 1 : 0;
  160. }
  161. const char *
  162. JackALSARawMidiPort::GetName()
  163. {
  164. return name;
  165. }
  166. int
  167. JackALSARawMidiPort::GetPollDescriptorCount()
  168. {
  169. return alsa_poll_fd_count + 1;
  170. }
  171. int
  172. JackALSARawMidiPort::GetQueuePollEvent()
  173. {
  174. unsigned short events = queue_poll_fd->revents;
  175. if (events & POLLNVAL) {
  176. jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file "
  177. "descriptor is invalid.");
  178. return -1;
  179. }
  180. if (events & POLLERR) {
  181. jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has "
  182. "occurred on the device or stream.");
  183. return -1;
  184. }
  185. int event = events & POLLIN ? 1 : 0;
  186. if (event) {
  187. char c;
  188. ssize_t result = read(fds[0], &c, 1);
  189. assert(result);
  190. if (result < 0) {
  191. jack_error("JackALSARawMidiPort::GetQueuePollEvents - error "
  192. "reading a byte from the pipe file descriptor: %s",
  193. strerror(errno));
  194. return -1;
  195. }
  196. }
  197. return event;
  198. }
  199. void
  200. JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd)
  201. {
  202. alsa_poll_fds = poll_fd + 1;
  203. assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds,
  204. alsa_poll_fd_count) ==
  205. alsa_poll_fd_count);
  206. queue_poll_fd = poll_fd;
  207. queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL;
  208. queue_poll_fd->fd = fds[0];
  209. SetIOEventsEnabled(true);
  210. }
  211. void
  212. JackALSARawMidiPort::SetIOEventsEnabled(bool enabled)
  213. {
  214. unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0);
  215. for (int i = 0; i < alsa_poll_fd_count; i++) {
  216. (alsa_poll_fds + i)->events = mask;
  217. }
  218. }
  219. bool
  220. JackALSARawMidiPort::TriggerQueueEvent()
  221. {
  222. char c;
  223. ssize_t result = write(fds[1], &c, 1);
  224. assert(result <= 1);
  225. switch (result) {
  226. case 1:
  227. return true;
  228. case 0:
  229. jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a "
  230. "byte to the pipe file descriptor: %s", strerror(errno));
  231. break;
  232. default:
  233. jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a "
  234. "byte to the pipe file descriptor.");
  235. }
  236. return false;
  237. }