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