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.

247 lines
7.3KB

  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. code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1);
  74. if (code) {
  75. error_message = snd_strerror(code);
  76. func = "snd_rawmidi_params_set_no_active_sensing";
  77. goto free_params;
  78. }
  79. code = snd_rawmidi_params(rawmidi, params);
  80. if (code) {
  81. error_message = snd_strerror(code);
  82. func = "snd_rawmidi_params";
  83. goto free_params;
  84. }
  85. snd_rawmidi_params_free(params);
  86. alsa_poll_fd_count = snd_rawmidi_poll_descriptors_count(rawmidi);
  87. if (! alsa_poll_fd_count) {
  88. error_message = "returned '0' count for poll descriptors";
  89. func = "snd_rawmidi_poll_descriptors_count";
  90. goto close;
  91. }
  92. try {
  93. CreateNonBlockingPipe(fds);
  94. } catch (std::exception e) {
  95. error_message = e.what();
  96. func = "CreateNonBlockingPipe";
  97. goto close;
  98. }
  99. snprintf(alias, sizeof(alias), "%s%d", alias_prefix, index + 1);
  100. snprintf(name, sizeof(name), "system:%d-%d %s %d %s", card + 1, device + 1,
  101. snd_rawmidi_info_get_name(info), subdevice + 1, name_suffix);
  102. this->io_mask = io_mask;
  103. return;
  104. free_params:
  105. snd_rawmidi_params_free(params);
  106. close:
  107. snd_rawmidi_close(rawmidi);
  108. handle_error:
  109. throw std::runtime_error(std::string(func) + ": " + error_message);
  110. }
  111. JackALSARawMidiPort::~JackALSARawMidiPort()
  112. {
  113. DestroyNonBlockingPipe(fds);
  114. if (rawmidi) {
  115. int code = snd_rawmidi_close(rawmidi);
  116. if (code) {
  117. jack_error("JackALSARawMidiPort::~JackALSARawMidiPort - "
  118. "snd_rawmidi_close: %s", snd_strerror(code));
  119. }
  120. rawmidi = 0;
  121. }
  122. }
  123. const char *
  124. JackALSARawMidiPort::GetAlias()
  125. {
  126. return alias;
  127. }
  128. int
  129. JackALSARawMidiPort::GetIOPollEvent()
  130. {
  131. unsigned short events;
  132. int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds,
  133. alsa_poll_fd_count,
  134. &events);
  135. if (code) {
  136. jack_error("JackALSARawMidiPort::GetIOPollEvents - "
  137. "snd_rawmidi_poll_descriptors_revents: %s",
  138. snd_strerror(code));
  139. return -1;
  140. }
  141. if (events & POLLNVAL) {
  142. jack_error("JackALSARawMidiPort::GetIOPollEvents - the file "
  143. "descriptor is invalid.");
  144. return -1;
  145. }
  146. if (events & POLLERR) {
  147. jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has "
  148. "occurred on the device or stream.");
  149. return -1;
  150. }
  151. return (events & io_mask) ? 1 : 0;
  152. }
  153. const char *
  154. JackALSARawMidiPort::GetName()
  155. {
  156. return name;
  157. }
  158. int
  159. JackALSARawMidiPort::GetPollDescriptorCount()
  160. {
  161. return alsa_poll_fd_count + 1;
  162. }
  163. int
  164. JackALSARawMidiPort::GetQueuePollEvent()
  165. {
  166. unsigned short events = queue_poll_fd->revents;
  167. if (events & POLLNVAL) {
  168. jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file "
  169. "descriptor is invalid.");
  170. return -1;
  171. }
  172. if (events & POLLERR) {
  173. jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has "
  174. "occurred on the device or stream.");
  175. return -1;
  176. }
  177. int event = events & POLLIN ? 1 : 0;
  178. if (event) {
  179. char c;
  180. ssize_t result = read(fds[0], &c, 1);
  181. assert(result);
  182. if (result < 0) {
  183. jack_error("JackALSARawMidiPort::GetQueuePollEvents - error "
  184. "reading a byte from the pipe file descriptor: %s",
  185. strerror(errno));
  186. return -1;
  187. }
  188. }
  189. return event;
  190. }
  191. void
  192. JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd)
  193. {
  194. alsa_poll_fds = poll_fd + 1;
  195. assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds,
  196. alsa_poll_fd_count) ==
  197. alsa_poll_fd_count);
  198. queue_poll_fd = poll_fd;
  199. queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL;
  200. queue_poll_fd->fd = fds[0];
  201. SetIOEventsEnabled(true);
  202. }
  203. void
  204. JackALSARawMidiPort::SetIOEventsEnabled(bool enabled)
  205. {
  206. unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0);
  207. for (int i = 0; i < alsa_poll_fd_count; i++) {
  208. (alsa_poll_fds + i)->events = mask;
  209. }
  210. }
  211. bool
  212. JackALSARawMidiPort::TriggerQueueEvent()
  213. {
  214. char c;
  215. ssize_t result = write(fds[1], &c, 1);
  216. assert(result <= 1);
  217. switch (result) {
  218. case 1:
  219. return true;
  220. case 0:
  221. jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a "
  222. "byte to the pipe file descriptor: %s", strerror(errno));
  223. break;
  224. default:
  225. jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a "
  226. "byte to the pipe file descriptor.");
  227. }
  228. return false;
  229. }