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