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