Jack2  1.9.8
JackALSARawMidiDriver.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 <memory>
21 #include <new>
22 #include <stdexcept>
23 
24 #include <alsa/asoundlib.h>
25 
26 #include "JackALSARawMidiDriver.h"
27 #include "JackALSARawMidiUtil.h"
28 #include "JackEngineControl.h"
29 #include "JackError.h"
30 #include "JackMidiUtil.h"
31 
33 
34 JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name,
35  const char *alias,
36  JackLockedEngine *engine,
37  JackSynchro *table):
38  JackMidiDriver(name, alias, engine, table)
39 {
40  thread = new JackThread(this);
41  fds[0] = -1;
42  fds[1] = -1;
43  input_ports = 0;
44  output_ports = 0;
45  output_port_timeouts = 0;
46  poll_fds = 0;
47 }
48 
49 JackALSARawMidiDriver::~JackALSARawMidiDriver()
50 {
51  delete thread;
52 }
53 
54 int
55 JackALSARawMidiDriver::Attach()
56 {
57  const char *alias;
58  jack_nframes_t buffer_size = fEngineControl->fBufferSize;
59  jack_port_id_t index;
60  jack_nframes_t latency = buffer_size;
61  jack_latency_range_t latency_range;
62  const char *name;
63  JackPort *port;
64  latency_range.max = latency;
65  latency_range.min = latency;
66  for (int i = 0; i < fCaptureChannels; i++) {
67  JackALSARawMidiInputPort *input_port = input_ports[i];
68  name = input_port->GetName();
69  fEngine->PortRegister(fClientControl.fRefNum, name,
70  JACK_DEFAULT_MIDI_TYPE,
71  CaptureDriverFlags, buffer_size, &index);
72  if (index == NO_PORT) {
73  jack_error("JackALSARawMidiDriver::Attach - cannot register input "
74  "port with name '%s'.", name);
75  // XX: Do we need to deallocate ports?
76  return -1;
77  }
78  alias = input_port->GetAlias();
79  port = fGraphManager->GetPort(index);
80  port->SetAlias(alias);
81  port->SetLatencyRange(JackCaptureLatency, &latency_range);
82  fCapturePortList[i] = index;
83 
84  jack_info("JackALSARawMidiDriver::Attach - input port registered "
85  "(name='%s', alias='%s').", name, alias);
86  }
87  if (! fEngineControl->fSyncMode) {
88  latency += buffer_size;
89  latency_range.max = latency;
90  latency_range.min = latency;
91  }
92  for (int i = 0; i < fPlaybackChannels; i++) {
93  JackALSARawMidiOutputPort *output_port = output_ports[i];
94  name = output_port->GetName();
95  fEngine->PortRegister(fClientControl.fRefNum, name,
96  JACK_DEFAULT_MIDI_TYPE,
97  PlaybackDriverFlags, buffer_size, &index);
98  if (index == NO_PORT) {
99  jack_error("JackALSARawMidiDriver::Attach - cannot register "
100  "output port with name '%s'.", name);
101  // XX: Do we need to deallocate ports?
102  return -1;
103  }
104  alias = output_port->GetAlias();
105  port = fGraphManager->GetPort(index);
106  port->SetAlias(alias);
107  port->SetLatencyRange(JackPlaybackLatency, &latency_range);
108  fPlaybackPortList[i] = index;
109 
110  jack_info("JackALSARawMidiDriver::Attach - output port registered "
111  "(name='%s', alias='%s').", name, alias);
112  }
113  return 0;
114 }
115 
116 int
117 JackALSARawMidiDriver::Close()
118 {
119  // Generic MIDI driver close
120  int result = JackMidiDriver::Close();
121 
122  if (input_ports) {
123  for (int i = 0; i < fCaptureChannels; i++) {
124  delete input_ports[i];
125  }
126  delete[] input_ports;
127  input_ports = 0;
128  }
129  if (output_ports) {
130  for (int i = 0; i < fPlaybackChannels; i++) {
131  delete output_ports[i];
132  }
133  delete[] output_ports;
134  output_ports = 0;
135  }
136  return result;
137 }
138 
139 bool
140 JackALSARawMidiDriver::Execute()
141 {
142  jack_nframes_t timeout_frame = 0;
143  for (;;) {
144  struct timespec timeout;
145  struct timespec *timeout_ptr;
146  if (! timeout_frame) {
147  timeout_ptr = 0;
148  } else {
149 
150  // The timeout value is relative to the time that
151  // 'GetMicroSeconds()' is called, not the time that 'poll()' is
152  // called. This means that the amount of time that passes between
153  // 'GetMicroSeconds()' and 'ppoll()' is time that will be lost
154  // while waiting for 'poll() to timeout.
155  //
156  // I tried to replace the timeout with a 'timerfd' with absolute
157  // times, but, strangely, it actually slowed things down, and made
158  // the code a lot more complicated.
159  //
160  // I wonder about using the 'epoll' interface instead of 'ppoll()'.
161  // The problem with the 'epoll' interface is that the timeout
162  // resolution of 'epoll_wait()' is set in milliseconds. We need
163  // microsecond resolution. Without microsecond resolution, we
164  // impose the same jitter as USB MIDI.
165  //
166  // Another problem is that 'ppoll()' returns later than the wait
167  // time. The problem can be minimized with high precision timers.
168 
169  timeout_ptr = &timeout;
170  jack_time_t next_time = GetTimeFromFrames(timeout_frame);
171  jack_time_t now = GetMicroSeconds();
172  if (next_time <= now) {
173  timeout.tv_sec = 0;
174  timeout.tv_nsec = 0;
175  } else {
176  jack_time_t wait_time = next_time - now;
177  timeout.tv_sec = wait_time / 1000000;
178  timeout.tv_nsec = (wait_time % 1000000) * 1000;
179  }
180  }
181  int poll_result = ppoll(poll_fds, poll_fd_count, timeout_ptr, 0);
182 
183  // Getting the current frame value here allows us to use it for
184  // incoming MIDI bytes. This makes sense, as the data has already
185  // arrived at this point.
186  jack_nframes_t current_frame = GetCurrentFrame();
187 
188  if (poll_result == -1) {
189  if (errno == EINTR) {
190  continue;
191  }
192  jack_error("JackALSARawMidiDriver::Execute - poll error: %s",
193  strerror(errno));
194  break;
195  }
196  jack_nframes_t port_timeout;
197  timeout_frame = 0;
198  if (! poll_result) {
199 
200  // No I/O events occurred. So, only handle timeout events on
201  // output ports.
202 
203  for (int i = 0; i < fPlaybackChannels; i++) {
204  port_timeout = output_port_timeouts[i];
205  if (port_timeout && (port_timeout <= current_frame)) {
206  if (! output_ports[i]->ProcessPollEvents(false, true,
207  &port_timeout)) {
208  jack_error("JackALSARawMidiDriver::Execute - a fatal "
209  "error occurred while processing ALSA "
210  "output events.");
211  goto cleanup;
212  }
213  output_port_timeouts[i] = port_timeout;
214  }
215  if (port_timeout && ((! timeout_frame) ||
216  (port_timeout < timeout_frame))) {
217  timeout_frame = port_timeout;
218  }
219  }
220  continue;
221  }
222 
223  // See if it's time to shutdown.
224 
225  unsigned short revents = poll_fds[0].revents;
226  if (revents) {
227  if (revents & (~ POLLHUP)) {
228  jack_error("JackALSARawMidiDriver::Execute - unexpected poll "
229  "event on pipe file descriptor.");
230  }
231  break;
232  }
233 
234  // Handle I/O events *and* timeout events on output ports.
235 
236  for (int i = 0; i < fPlaybackChannels; i++) {
237  port_timeout = output_port_timeouts[i];
238  bool timeout = port_timeout && (port_timeout <= current_frame);
239  if (! output_ports[i]->ProcessPollEvents(true, timeout,
240  &port_timeout)) {
241  jack_error("JackALSARawMidiDriver::Execute - a fatal error "
242  "occurred while processing ALSA output events.");
243  goto cleanup;
244  }
245  output_port_timeouts[i] = port_timeout;
246  if (port_timeout && ((! timeout_frame) ||
247  (port_timeout < timeout_frame))) {
248  timeout_frame = port_timeout;
249  }
250  }
251 
252  // Handle I/O events on input ports. We handle these last because we
253  // already computed the arrival time above, and will impose a delay on
254  // the events by 'period-size' frames anyway, which gives us a bit of
255  // borrowed time.
256 
257  for (int i = 0; i < fCaptureChannels; i++) {
258  if (! input_ports[i]->ProcessPollEvents(current_frame)) {
259  jack_error("JackALSARawMidiDriver::Execute - a fatal error "
260  "occurred while processing ALSA input events.");
261  goto cleanup;
262  }
263  }
264  }
265  cleanup:
266  close(fds[0]);
267  fds[0] = -1;
268 
269  jack_info("JackALSARawMidiDriver::Execute - ALSA thread exiting.");
270 
271  return false;
272 }
273 
274 void
275 JackALSARawMidiDriver::
276 FreeDeviceInfo(std::vector<snd_rawmidi_info_t *> *in_info_list,
277  std::vector<snd_rawmidi_info_t *> *out_info_list)
278 {
279  size_t length = in_info_list->size();
280  for (size_t i = 0; i < length; i++) {
281  snd_rawmidi_info_free(in_info_list->at(i));
282  }
283  length = out_info_list->size();
284  for (size_t i = 0; i < length; i++) {
285  snd_rawmidi_info_free(out_info_list->at(i));
286  }
287 }
288 
289 void
290 JackALSARawMidiDriver::
291 GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info,
292  std::vector<snd_rawmidi_info_t *> *info_list)
293 {
294  snd_rawmidi_info_set_subdevice(info, 0);
295  int code = snd_ctl_rawmidi_info(control, info);
296  if (code) {
297  if (code != -ENOENT) {
298  HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
299  }
300  return;
301  }
302  unsigned int count = snd_rawmidi_info_get_subdevices_count(info);
303  for (unsigned int i = 0; i < count; i++) {
304  snd_rawmidi_info_set_subdevice(info, i);
305  int code = snd_ctl_rawmidi_info(control, info);
306  if (code) {
307  HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
308  continue;
309  }
310  snd_rawmidi_info_t *info_copy;
311  code = snd_rawmidi_info_malloc(&info_copy);
312  if (code) {
313  HandleALSAError("GetDeviceInfo", "snd_rawmidi_info_malloc", code);
314  continue;
315  }
316  snd_rawmidi_info_copy(info_copy, info);
317  try {
318  info_list->push_back(info_copy);
319  } catch (std::bad_alloc &e) {
320  snd_rawmidi_info_free(info_copy);
321  jack_error("JackALSARawMidiDriver::GetDeviceInfo - "
322  "std::vector::push_back: %s", e.what());
323  }
324  }
325 }
326 
327 void
328 JackALSARawMidiDriver::HandleALSAError(const char *driver_func,
329  const char *alsa_func, int code)
330 {
331  jack_error("JackALSARawMidiDriver::%s - %s: %s", driver_func, alsa_func,
332  snd_strerror(code));
333 }
334 
335 bool
337 {
338  set_threaded_log_function();
339  if (thread->AcquireSelfRealTime(fEngineControl->fServerPriority + 1)) {
340  jack_error("JackALSARawMidiDriver::Init - could not acquire realtime "
341  "scheduling. Continuing anyway.");
342  }
343  return true;
344 }
345 
346 int
347 JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels,
348  int out_channels, bool monitor,
349  const char *capture_driver_name,
350  const char *playback_driver_name,
351  jack_nframes_t capture_latency,
352  jack_nframes_t playback_latency)
353 {
354  snd_rawmidi_info_t *info;
355  int code = snd_rawmidi_info_malloc(&info);
356  if (code) {
357  HandleALSAError("Open", "snd_rawmidi_info_malloc", code);
358  return -1;
359  }
360  std::vector<snd_rawmidi_info_t *> in_info_list;
361  std::vector<snd_rawmidi_info_t *> out_info_list;
362  for (int card = -1;;) {
363  int code = snd_card_next(&card);
364  if (code) {
365  HandleALSAError("Open", "snd_card_next", code);
366  continue;
367  }
368  if (card == -1) {
369  break;
370  }
371  char name[32];
372  snprintf(name, sizeof(name), "hw:%d", card);
373  snd_ctl_t *control;
374  code = snd_ctl_open(&control, name, SND_CTL_NONBLOCK);
375  if (code) {
376  HandleALSAError("Open", "snd_ctl_open", code);
377  continue;
378  }
379  for (int device = -1;;) {
380  code = snd_ctl_rawmidi_next_device(control, &device);
381  if (code) {
382  HandleALSAError("Open", "snd_ctl_rawmidi_next_device", code);
383  continue;
384  }
385  if (device == -1) {
386  break;
387  }
388  snd_rawmidi_info_set_device(info, device);
389  snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
390  GetDeviceInfo(control, info, &in_info_list);
391  snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT);
392  GetDeviceInfo(control, info, &out_info_list);
393  }
394  snd_ctl_close(control);
395  }
396  snd_rawmidi_info_free(info);
397  size_t potential_inputs = in_info_list.size();
398  size_t potential_outputs = out_info_list.size();
399  if (! (potential_inputs || potential_outputs)) {
400  jack_error("JackALSARawMidiDriver::Open - no ALSA raw MIDI input or "
401  "output ports found.");
402  FreeDeviceInfo(&in_info_list, &out_info_list);
403  return -1;
404  }
405  size_t num_inputs = 0;
406  size_t num_outputs = 0;
407  if (potential_inputs) {
408  try {
409  input_ports = new JackALSARawMidiInputPort *[potential_inputs];
410  } catch (std::exception e) {
411  jack_error("JackALSARawMidiDriver::Open - while creating input "
412  "port array: %s", e.what());
413  FreeDeviceInfo(&in_info_list, &out_info_list);
414  return -1;
415  }
416  }
417  if (potential_outputs) {
418  try {
419  output_ports = new JackALSARawMidiOutputPort *[potential_outputs];
420  } catch (std::exception e) {
421  jack_error("JackALSARawMidiDriver::Open - while creating output "
422  "port array: %s", e.what());
423  FreeDeviceInfo(&in_info_list, &out_info_list);
424  goto delete_input_ports;
425  }
426  }
427  for (size_t i = 0; i < potential_inputs; i++) {
428  snd_rawmidi_info_t *info = in_info_list.at(i);
429  try {
430  input_ports[num_inputs] = new JackALSARawMidiInputPort(info, i);
431  num_inputs++;
432  } catch (std::exception e) {
433  jack_error("JackALSARawMidiDriver::Open - while creating new "
434  "JackALSARawMidiInputPort: %s", e.what());
435  }
436  snd_rawmidi_info_free(info);
437  }
438  for (size_t i = 0; i < potential_outputs; i++) {
439  snd_rawmidi_info_t *info = out_info_list.at(i);
440  try {
441  output_ports[num_outputs] = new JackALSARawMidiOutputPort(info, i);
442  num_outputs++;
443  } catch (std::exception e) {
444  jack_error("JackALSARawMidiDriver::Open - while creating new "
445  "JackALSARawMidiOutputPort: %s", e.what());
446  }
447  snd_rawmidi_info_free(info);
448  }
449  if (! (num_inputs || num_outputs)) {
450  jack_error("JackALSARawMidiDriver::Open - none of the potential "
451  "inputs or outputs were successfully opened.");
452  } else if (JackMidiDriver::Open(capturing, playing, num_inputs,
453  num_outputs, monitor, capture_driver_name,
454  playback_driver_name, capture_latency,
455  playback_latency)) {
456  jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error");
457  } else {
458  return 0;
459  }
460  if (output_ports) {
461  for (size_t i = 0; i < num_outputs; i++) {
462  delete output_ports[i];
463  }
464  delete[] output_ports;
465  output_ports = 0;
466  }
467  delete_input_ports:
468  if (input_ports) {
469  for (size_t i = 0; i < num_inputs; i++) {
470  delete input_ports[i];
471  }
472  delete[] input_ports;
473  input_ports = 0;
474  }
475  return -1;
476 }
477 
478 int
479 JackALSARawMidiDriver::Read()
480 {
481  jack_nframes_t buffer_size = fEngineControl->fBufferSize;
482  for (int i = 0; i < fCaptureChannels; i++) {
483  if (! input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size)) {
484  return -1;
485  }
486  }
487  return 0;
488 }
489 
490 int
491 JackALSARawMidiDriver::Start()
492 {
493 
494  jack_info("JackALSARawMidiDriver::Start - Starting 'alsarawmidi' driver.");
495 
496  JackMidiDriver::Start();
497  poll_fd_count = 1;
498  for (int i = 0; i < fCaptureChannels; i++) {
499  poll_fd_count += input_ports[i]->GetPollDescriptorCount();
500  }
501  for (int i = 0; i < fPlaybackChannels; i++) {
502  poll_fd_count += output_ports[i]->GetPollDescriptorCount();
503  }
504  try {
505  poll_fds = new pollfd[poll_fd_count];
506  } catch (std::exception e) {
507  jack_error("JackALSARawMidiDriver::Start - creating poll descriptor "
508  "structures failed: %s", e.what());
509  return -1;
510  }
511  if (fPlaybackChannels) {
512  try {
513  output_port_timeouts = new jack_nframes_t[fPlaybackChannels];
514  } catch (std::exception e) {
515  jack_error("JackALSARawMidiDriver::Start - creating array for "
516  "output port timeout values failed: %s", e.what());
517  goto free_poll_descriptors;
518  }
519  }
520  struct pollfd *poll_fd_iter;
521  try {
522  CreateNonBlockingPipe(fds);
523  } catch (std::exception e) {
524  jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: "
525  "%s", e.what());
526  goto free_output_port_timeouts;
527  }
528  poll_fds[0].events = POLLERR | POLLIN | POLLNVAL;
529  poll_fds[0].fd = fds[0];
530  poll_fd_iter = poll_fds + 1;
531  for (int i = 0; i < fCaptureChannels; i++) {
532  JackALSARawMidiInputPort *input_port = input_ports[i];
533  input_port->PopulatePollDescriptors(poll_fd_iter);
534  poll_fd_iter += input_port->GetPollDescriptorCount();
535  }
536  for (int i = 0; i < fPlaybackChannels; i++) {
537  JackALSARawMidiOutputPort *output_port = output_ports[i];
538  output_port->PopulatePollDescriptors(poll_fd_iter);
539  poll_fd_iter += output_port->GetPollDescriptorCount();
540  output_port_timeouts[i] = 0;
541  }
542 
543  jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ...");
544 
545  if (! thread->StartSync()) {
546 
547  jack_info("JackALSARawMidiDriver::Start - started ALSA thread.");
548 
549  return 0;
550  }
551  jack_error("JackALSARawMidiDriver::Start - failed to start MIDI "
552  "processing thread.");
553 
554  DestroyNonBlockingPipe(fds);
555  fds[1] = -1;
556  fds[0] = -1;
557  free_output_port_timeouts:
558  delete[] output_port_timeouts;
559  output_port_timeouts = 0;
560  free_poll_descriptors:
561  delete[] poll_fds;
562  poll_fds = 0;
563  return -1;
564 }
565 
566 int
567 JackALSARawMidiDriver::Stop()
568 {
569  jack_info("JackALSARawMidiDriver::Stop - stopping 'alsarawmidi' driver.");
570  JackMidiDriver::Stop();
571 
572  if (fds[1] != -1) {
573  close(fds[1]);
574  fds[1] = -1;
575  }
576  int result;
577  const char *verb;
578  switch (thread->GetStatus()) {
579  case JackThread::kIniting:
580  case JackThread::kStarting:
581  result = thread->Kill();
582  verb = "kill";
583  break;
584  case JackThread::kRunning:
585  result = thread->Stop();
586  verb = "stop";
587  break;
588  default:
589  result = 0;
590  verb = 0;
591  }
592  if (fds[0] != -1) {
593  close(fds[0]);
594  fds[0] = -1;
595  }
596  if (output_port_timeouts) {
597  delete[] output_port_timeouts;
598  output_port_timeouts = 0;
599  }
600  if (poll_fds) {
601  delete[] poll_fds;
602  poll_fds = 0;
603  }
604  if (result) {
605  jack_error("JackALSARawMidiDriver::Stop - could not %s MIDI "
606  "processing thread.", verb);
607  }
608  return result;
609 }
610 
611 int
612 JackALSARawMidiDriver::Write()
613 {
614  jack_nframes_t buffer_size = fEngineControl->fBufferSize;
615  for (int i = 0; i < fPlaybackChannels; i++) {
616  if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size)) {
617  return -1;
618  }
619  }
620  return 0;
621 }
622 
623 #ifdef __cplusplus
624 extern "C" {
625 #endif
626 
627  SERVER_EXPORT jack_driver_desc_t *
628  driver_get_descriptor()
629  {
630  // X: There could be parameters here regarding setting I/O buffer
631  // sizes. I don't think MIDI drivers can accept parameters right
632  // now without being set as the main driver.
633 
634  return jack_driver_descriptor_construct("alsarawmidi", JackDriverSlave, "Alternative ALSA raw MIDI backend.", NULL);
635  }
636 
637  SERVER_EXPORT Jack::JackDriverClientInterface *
638  driver_initialize(Jack::JackLockedEngine *engine, Jack::JackSynchro *table,
639  const JSList *params)
640  {
642  new Jack::JackALSARawMidiDriver("system_midi", "alsarawmidi",
643  engine, table);
644  if (driver->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0)) {
645  delete driver;
646  driver = 0;
647  }
648  return driver;
649  }
650 
651 #ifdef __cplusplus
652 }
653 #endif