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
JackALSARawMidiDriver(const char * name,const char * alias,JackLockedEngine * engine,JackSynchro * table)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
~JackALSARawMidiDriver()50 JackALSARawMidiDriver::~JackALSARawMidiDriver()
51 {
52 delete thread;
53 }
54
55 int
Attach()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->PortSetDefaultMetadata(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->PortSetDefaultMetadata(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
Close()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
Execute()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::
FreeDeviceInfo(std::vector<snd_rawmidi_info_t * > * in_info_list,std::vector<snd_rawmidi_info_t * > * out_info_list)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::
GetDeviceInfo(snd_ctl_t * control,snd_rawmidi_info_t * info,std::vector<snd_rawmidi_info_t * > * info_list)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
HandleALSAError(const char * driver_func,const char * alsa_func,int code)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
Init()341 JackALSARawMidiDriver::Init()
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
Open(bool capturing,bool playing,int in_channels,int out_channels,bool monitor,const char * capture_driver_name,const char * playback_driver_name,jack_nframes_t capture_latency,jack_nframes_t playback_latency)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
Read()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
Start()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
Stop()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
Write()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 *
driver_get_descriptor()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 *
driver_initialize(Jack::JackLockedEngine * engine,Jack::JackSynchro * table,const JSList * params)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
668