Jack2: JackALSARawMidiDriver.cpp Source File

Jack2 1.9.12

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
33 using Jack::JackALSARawMidiDriver;
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
337 JackALSARawMidiDriver::Init()
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 // singleton kind of driver
629 static Jack::JackALSARawMidiDriver* driver = NULL;
630
631 SERVER_EXPORT jack_driver_desc_t *
632 driver_get_descriptor()
633 {
634 // X: There could be parameters here regarding setting I/O buffer
635 // sizes. I don't think MIDI drivers can accept parameters right
636 // now without being set as the main driver.
637
638 return jack_driver_descriptor_construct("alsarawmidi", JackDriverSlave, "Alternative ALSA raw MIDI backend.", NULL);
639 }
640
641 SERVER_EXPORT Jack::JackDriverClientInterface *
642 driver_initialize(Jack::JackLockedEngine *engine, Jack::JackSynchro *table,
643 const JSList *params)
644 {
645 // singleton kind of driver
646 if (!driver) {
647 driver = new Jack::JackALSARawMidiDriver("system_midi", "alsarawmidi", engine, table);
648 if (driver->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0) == 0) {
649 return driver;
650 } else {
651 delete driver;
652 return NULL;
653 }
654 } else {
655 jack_info("JackALSARawMidiDriver already allocated, cannot be loaded twice");
656 return NULL;
657 }
658 }
659
660 #ifdef __cplusplus
661 }
662 #endif
Jack::JackALSARawMidiDriver
Definition: JackALSARawMidiDriver.h:35
Jack::JackLockedEngine
Locked Engine, access to methods is serialized using a mutex.
Definition: JackLockedEngine.h:78
Jack::JackPosixSemaphore
Inter process synchronization using POSIX semaphore.
Definition: JackPosixSemaphore.h:36
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
_jack_latency_range::min
jack_nframes_t min
Definition: types.h:270
jack_info
SERVER_EXPORT void jack_info(const char *fmt,...)
Definition: JackError.cpp:100
Jack::JackALSARawMidiDriver::Init
bool Init()
Definition: JackALSARawMidiDriver.cpp:337
_jack_latency_range
Definition: types.h:265
_jack_latency_range::max
jack_nframes_t max
Definition: types.h:274
Jack::JackDriverClientInterface
The base interface for drivers clients.
Definition: JackDriver.h:114
_JSList
Definition: linux/alsa/jslist.h:35
jack_driver_desc_t
Definition: driver_interface.h:132