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