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.

256 lines
7.5KB

  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_prefix;
  32. const char *error_message;
  33. snd_rawmidi_t **in;
  34. snd_rawmidi_t **out;
  35. const char *name_suffix;
  36. if (snd_rawmidi_info_get_stream(info) == SND_RAWMIDI_STREAM_OUTPUT) {
  37. alias_prefix = "system:midi_playback_";
  38. in = 0;
  39. name_suffix = "out";
  40. out = &rawmidi;
  41. } else {
  42. alias_prefix = "system:midi_capture_";
  43. in = &rawmidi;
  44. name_suffix = "in";
  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), "%s%d", alias_prefix, index + 1);
  107. snprintf(name, sizeof(name), "system:%d-%d %s %d %s", card + 1, device + 1,
  108. snd_rawmidi_info_get_name(info), subdevice + 1, name_suffix);
  109. this->io_mask = io_mask;
  110. return;
  111. free_params:
  112. snd_rawmidi_params_free(params);
  113. close:
  114. snd_rawmidi_close(rawmidi);
  115. handle_error:
  116. throw std::runtime_error(std::string(func) + ": " + error_message);
  117. }
  118. JackALSARawMidiPort::~JackALSARawMidiPort()
  119. {
  120. DestroyNonBlockingPipe(fds);
  121. if (rawmidi) {
  122. int code = snd_rawmidi_close(rawmidi);
  123. if (code) {
  124. jack_error("JackALSARawMidiPort::~JackALSARawMidiPort - "
  125. "snd_rawmidi_close: %s", snd_strerror(code));
  126. }
  127. rawmidi = 0;
  128. }
  129. }
  130. const char *
  131. JackALSARawMidiPort::GetAlias()
  132. {
  133. return alias;
  134. }
  135. int
  136. JackALSARawMidiPort::GetIOPollEvent()
  137. {
  138. unsigned short events;
  139. int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds,
  140. alsa_poll_fd_count,
  141. &events);
  142. if (code) {
  143. jack_error("JackALSARawMidiPort::GetIOPollEvents - "
  144. "snd_rawmidi_poll_descriptors_revents: %s",
  145. snd_strerror(code));
  146. return -1;
  147. }
  148. if (events & POLLNVAL) {
  149. jack_error("JackALSARawMidiPort::GetIOPollEvents - the file "
  150. "descriptor is invalid.");
  151. return -1;
  152. }
  153. if (events & POLLERR) {
  154. jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has "
  155. "occurred on the device or stream.");
  156. return -1;
  157. }
  158. return (events & io_mask) ? 1 : 0;
  159. }
  160. const char *
  161. JackALSARawMidiPort::GetName()
  162. {
  163. return name;
  164. }
  165. int
  166. JackALSARawMidiPort::GetPollDescriptorCount()
  167. {
  168. return alsa_poll_fd_count + 1;
  169. }
  170. int
  171. JackALSARawMidiPort::GetQueuePollEvent()
  172. {
  173. unsigned short events = queue_poll_fd->revents;
  174. if (events & POLLNVAL) {
  175. jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file "
  176. "descriptor is invalid.");
  177. return -1;
  178. }
  179. if (events & POLLERR) {
  180. jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has "
  181. "occurred on the device or stream.");
  182. return -1;
  183. }
  184. int event = events & POLLIN ? 1 : 0;
  185. if (event) {
  186. char c;
  187. ssize_t result = read(fds[0], &c, 1);
  188. assert(result);
  189. if (result < 0) {
  190. jack_error("JackALSARawMidiPort::GetQueuePollEvents - error "
  191. "reading a byte from the pipe file descriptor: %s",
  192. strerror(errno));
  193. return -1;
  194. }
  195. }
  196. return event;
  197. }
  198. void
  199. JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd)
  200. {
  201. alsa_poll_fds = poll_fd + 1;
  202. assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds,
  203. alsa_poll_fd_count) ==
  204. alsa_poll_fd_count);
  205. queue_poll_fd = poll_fd;
  206. queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL;
  207. queue_poll_fd->fd = fds[0];
  208. SetIOEventsEnabled(true);
  209. }
  210. void
  211. JackALSARawMidiPort::SetIOEventsEnabled(bool enabled)
  212. {
  213. unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0);
  214. for (int i = 0; i < alsa_poll_fd_count; i++) {
  215. (alsa_poll_fds + i)->events = mask;
  216. }
  217. }
  218. bool
  219. JackALSARawMidiPort::TriggerQueueEvent()
  220. {
  221. char c;
  222. ssize_t result = write(fds[1], &c, 1);
  223. assert(result <= 1);
  224. switch (result) {
  225. case 1:
  226. return true;
  227. case 0:
  228. jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a "
  229. "byte to the pipe file descriptor: %s", strerror(errno));
  230. break;
  231. default:
  232. jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a "
  233. "byte to the pipe file descriptor.");
  234. }
  235. return false;
  236. }