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.

265 lines
7.8KB

  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(const char *client_name, 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* driver_name = snd_rawmidi_info_get_name(info);
  32. const char *alias_suffix;
  33. const char *error_message;
  34. snd_rawmidi_t **in;
  35. const char *port_name;
  36. snd_rawmidi_t **out;
  37. if (snd_rawmidi_info_get_stream(info) == SND_RAWMIDI_STREAM_OUTPUT) {
  38. alias_suffix = "out";
  39. in = 0;
  40. port_name = "playback_";
  41. out = &rawmidi;
  42. } else {
  43. alias_suffix = "in";
  44. in = &rawmidi;
  45. port_name = "capture_";
  46. out = 0;
  47. }
  48. const char *func;
  49. int code = snd_rawmidi_open(in, out, device_id, SND_RAWMIDI_NONBLOCK);
  50. if (code) {
  51. error_message = snd_strerror(code);
  52. func = "snd_rawmidi_open";
  53. goto handle_error;
  54. }
  55. snd_rawmidi_params_t *params;
  56. code = snd_rawmidi_params_malloc(&params);
  57. if (code) {
  58. error_message = snd_strerror(code);
  59. func = "snd_rawmidi_params_malloc";
  60. goto close;
  61. }
  62. code = snd_rawmidi_params_current(rawmidi, params);
  63. if (code) {
  64. error_message = snd_strerror(code);
  65. func = "snd_rawmidi_params_current";
  66. goto free_params;
  67. }
  68. code = snd_rawmidi_params_set_avail_min(rawmidi, params, 1);
  69. if (code) {
  70. error_message = snd_strerror(code);
  71. func = "snd_rawmidi_params_set_avail_min";
  72. goto free_params;
  73. }
  74. // Minimum buffer size allowed by ALSA
  75. code = snd_rawmidi_params_set_buffer_size(rawmidi, params, 32);
  76. if (code) {
  77. error_message = snd_strerror(code);
  78. func = "snd_rawmidi_params_set_buffer_size";
  79. goto free_params;
  80. }
  81. code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1);
  82. if (code) {
  83. error_message = snd_strerror(code);
  84. func = "snd_rawmidi_params_set_no_active_sensing";
  85. goto free_params;
  86. }
  87. code = snd_rawmidi_params(rawmidi, params);
  88. if (code) {
  89. error_message = snd_strerror(code);
  90. func = "snd_rawmidi_params";
  91. goto free_params;
  92. }
  93. snd_rawmidi_params_free(params);
  94. alsa_poll_fd_count = snd_rawmidi_poll_descriptors_count(rawmidi);
  95. if (! alsa_poll_fd_count) {
  96. error_message = "returned '0' count for poll descriptors";
  97. func = "snd_rawmidi_poll_descriptors_count";
  98. goto close;
  99. }
  100. try {
  101. CreateNonBlockingPipe(fds);
  102. } catch (std::exception& e) {
  103. error_message = e.what();
  104. func = "CreateNonBlockingPipe";
  105. goto close;
  106. }
  107. snprintf(alias, sizeof(alias), "system:%d-%d %s %d %s", card + 1,
  108. device + 1, driver_name, subdevice + 1,
  109. alias_suffix);
  110. snprintf(name, sizeof(name), "%s:%s%zu", client_name, port_name, index + 1);
  111. strncpy(device_name, driver_name, sizeof(device_name) - 1);
  112. this->io_mask = io_mask;
  113. return;
  114. free_params:
  115. snd_rawmidi_params_free(params);
  116. close:
  117. snd_rawmidi_close(rawmidi);
  118. handle_error:
  119. throw std::runtime_error(std::string(func) + ": " + error_message);
  120. }
  121. JackALSARawMidiPort::~JackALSARawMidiPort()
  122. {
  123. DestroyNonBlockingPipe(fds);
  124. if (rawmidi) {
  125. int code = snd_rawmidi_close(rawmidi);
  126. if (code) {
  127. jack_error("JackALSARawMidiPort::~JackALSARawMidiPort - "
  128. "snd_rawmidi_close: %s", snd_strerror(code));
  129. }
  130. rawmidi = 0;
  131. }
  132. }
  133. const char *
  134. JackALSARawMidiPort::GetAlias()
  135. {
  136. return alias;
  137. }
  138. int
  139. JackALSARawMidiPort::GetIOPollEvent()
  140. {
  141. unsigned short events;
  142. int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds,
  143. alsa_poll_fd_count,
  144. &events);
  145. if (code) {
  146. jack_error("JackALSARawMidiPort::GetIOPollEvents - "
  147. "snd_rawmidi_poll_descriptors_revents: %s",
  148. snd_strerror(code));
  149. return -1;
  150. }
  151. if (events & POLLNVAL) {
  152. jack_error("JackALSARawMidiPort::GetIOPollEvents - the file "
  153. "descriptor is invalid.");
  154. return -1;
  155. }
  156. if (events & POLLERR) {
  157. jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has "
  158. "occurred on the device or stream.");
  159. return -1;
  160. }
  161. return (events & io_mask) ? 1 : 0;
  162. }
  163. const char *
  164. JackALSARawMidiPort::GetName()
  165. {
  166. return name;
  167. }
  168. const char *
  169. JackALSARawMidiPort::GetDeviceName()
  170. {
  171. return device_name;
  172. }
  173. int
  174. JackALSARawMidiPort::GetPollDescriptorCount()
  175. {
  176. return alsa_poll_fd_count + 1;
  177. }
  178. int
  179. JackALSARawMidiPort::GetQueuePollEvent()
  180. {
  181. unsigned short events = queue_poll_fd->revents;
  182. if (events & POLLNVAL) {
  183. jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file "
  184. "descriptor is invalid.");
  185. return -1;
  186. }
  187. if (events & POLLERR) {
  188. jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has "
  189. "occurred on the device or stream.");
  190. return -1;
  191. }
  192. int event = events & POLLIN ? 1 : 0;
  193. if (event) {
  194. char c;
  195. ssize_t result = read(fds[0], &c, 1);
  196. assert(result);
  197. if (result < 0) {
  198. jack_error("JackALSARawMidiPort::GetQueuePollEvents - error "
  199. "reading a byte from the pipe file descriptor: %s",
  200. strerror(errno));
  201. return -1;
  202. }
  203. }
  204. return event;
  205. }
  206. void
  207. JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd)
  208. {
  209. alsa_poll_fds = poll_fd + 1;
  210. assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds,
  211. alsa_poll_fd_count) ==
  212. alsa_poll_fd_count);
  213. queue_poll_fd = poll_fd;
  214. queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL;
  215. queue_poll_fd->fd = fds[0];
  216. SetIOEventsEnabled(true);
  217. }
  218. void
  219. JackALSARawMidiPort::SetIOEventsEnabled(bool enabled)
  220. {
  221. unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0);
  222. for (int i = 0; i < alsa_poll_fd_count; i++) {
  223. (alsa_poll_fds + i)->events = mask;
  224. }
  225. }
  226. bool
  227. JackALSARawMidiPort::TriggerQueueEvent()
  228. {
  229. char c;
  230. ssize_t result = write(fds[1], &c, 1);
  231. assert(result <= 1);
  232. switch (result) {
  233. case 1:
  234. return true;
  235. case 0:
  236. jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a "
  237. "byte to the pipe file descriptor: %s", strerror(errno));
  238. break;
  239. default:
  240. jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a "
  241. "byte to the pipe file descriptor.");
  242. }
  243. return false;
  244. }