2 Copyright (C) 2011 Devin Anderson
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.
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.
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.
24 #include <alsa/asoundlib.h>
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"
33 using Jack::JackALSARawMidiDriver
;
35 JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name
,
37 JackLockedEngine
*engine
,
39 JackMidiDriver(name
, alias
, engine
, table
)
41 thread
= new JackThread(this);
46 output_port_timeouts
= 0;
50 JackALSARawMidiDriver::~JackALSARawMidiDriver()
56 JackALSARawMidiDriver::Attach()
59 jack_nframes_t buffer_size
= fEngineControl
->fBufferSize
;
61 jack_nframes_t latency
= buffer_size
;
62 jack_latency_range_t latency_range
;
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?
79 alias
= input_port
->GetAlias();
80 port
= fGraphManager
->GetPort(index
);
81 port
->SetAlias(alias
);
82 port
->SetLatencyRange(JackCaptureLatency
, &latency_range
);
83 fCapturePortList
[i
] = index
;
85 jack_info("JackALSARawMidiDriver::Attach - input port registered "
86 "(name='%s', alias='%s').", name
, alias
);
88 if (! fEngineControl
->fSyncMode
) {
89 latency
+= buffer_size
;
90 latency_range
.max
= latency
;
91 latency_range
.min
= latency
;
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?
105 alias
= output_port
->GetAlias();
106 port
= fGraphManager
->GetPort(index
);
107 port
->SetAlias(alias
);
108 port
->SetLatencyRange(JackPlaybackLatency
, &latency_range
);
109 fPlaybackPortList
[i
] = index
;
111 jack_info("JackALSARawMidiDriver::Attach - output port registered "
112 "(name='%s', alias='%s').", name
, alias
);
118 JackALSARawMidiDriver::Close()
120 // Generic MIDI driver close
121 int result
= JackMidiDriver::Close();
124 for (int i
= 0; i
< fCaptureChannels
; i
++) {
125 delete input_ports
[i
];
127 delete[] input_ports
;
131 for (int i
= 0; i
< fPlaybackChannels
; i
++) {
132 delete output_ports
[i
];
134 delete[] output_ports
;
141 JackALSARawMidiDriver::Execute()
143 jack_nframes_t timeout_frame
= 0;
145 struct timespec timeout
;
146 struct timespec
*timeout_ptr
;
147 if (! timeout_frame
) {
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.
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.
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.
167 // Another problem is that 'ppoll()' returns later than the wait
168 // time. The problem can be minimized with high precision timers.
170 timeout_ptr
= &timeout
;
171 jack_time_t next_time
= GetTimeFromFrames(timeout_frame
);
172 jack_time_t now
= GetMicroSeconds();
173 if (next_time
<= now
) {
177 jack_time_t wait_time
= next_time
- now
;
178 timeout
.tv_sec
= wait_time
/ 1000000;
179 timeout
.tv_nsec
= (wait_time
% 1000000) * 1000;
182 int poll_result
= ppoll(poll_fds
, poll_fd_count
, timeout_ptr
, 0);
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();
189 if (poll_result
== -1) {
190 if (errno
== EINTR
) {
193 jack_error("JackALSARawMidiDriver::Execute - poll error: %s",
197 jack_nframes_t port_timeout
;
201 // No I/O events occurred. So, only handle timeout events on
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,
209 jack_error("JackALSARawMidiDriver::Execute - a fatal "
210 "error occurred while processing ALSA "
214 output_port_timeouts
[i
] = port_timeout
;
216 if (port_timeout
&& ((! timeout_frame
) ||
217 (port_timeout
< timeout_frame
))) {
218 timeout_frame
= port_timeout
;
224 // See if it's time to shutdown.
226 unsigned short revents
= poll_fds
[0].revents
;
228 if (revents
& (~ POLLHUP
)) {
229 jack_error("JackALSARawMidiDriver::Execute - unexpected poll "
230 "event on pipe file descriptor.");
235 // Handle I/O events *and* timeout events on output ports.
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
,
242 jack_error("JackALSARawMidiDriver::Execute - a fatal error "
243 "occurred while processing ALSA output events.");
246 output_port_timeouts
[i
] = port_timeout
;
247 if (port_timeout
&& ((! timeout_frame
) ||
248 (port_timeout
< timeout_frame
))) {
249 timeout_frame
= port_timeout
;
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
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.");
270 jack_info("JackALSARawMidiDriver::Execute - ALSA thread exiting.");
276 JackALSARawMidiDriver::
277 FreeDeviceInfo(std::vector
<snd_rawmidi_info_t
*> *in_info_list
,
278 std::vector
<snd_rawmidi_info_t
*> *out_info_list
)
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
));
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
));
291 JackALSARawMidiDriver::
292 GetDeviceInfo(snd_ctl_t
*control
, snd_rawmidi_info_t
*info
,
293 std::vector
<snd_rawmidi_info_t
*> *info_list
)
295 snd_rawmidi_info_set_subdevice(info
, 0);
296 int code
= snd_ctl_rawmidi_info(control
, info
);
298 if (code
!= -ENOENT
) {
299 HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code
);
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
);
308 HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code
);
311 snd_rawmidi_info_t
*info_copy
;
312 code
= snd_rawmidi_info_malloc(&info_copy
);
314 HandleALSAError("GetDeviceInfo", "snd_rawmidi_info_malloc", code
);
317 snd_rawmidi_info_copy(info_copy
, info
);
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());
329 JackALSARawMidiDriver::HandleALSAError(const char *driver_func
,
330 const char *alsa_func
, int code
)
332 jack_error("JackALSARawMidiDriver::%s - %s: %s", driver_func
, alsa_func
,
337 JackALSARawMidiDriver::Init()
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.");
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
)
355 snd_rawmidi_info_t
*info
;
356 int code
= snd_rawmidi_info_malloc(&info
);
358 HandleALSAError("Open", "snd_rawmidi_info_malloc", code
);
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
);
366 HandleALSAError("Open", "snd_card_next", code
);
373 snprintf(name
, sizeof(name
), "hw:%d", card
);
375 code
= snd_ctl_open(&control
, name
, SND_CTL_NONBLOCK
);
377 HandleALSAError("Open", "snd_ctl_open", code
);
380 for (int device
= -1;;) {
381 code
= snd_ctl_rawmidi_next_device(control
, &device
);
383 HandleALSAError("Open", "snd_ctl_rawmidi_next_device", code
);
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
);
395 snd_ctl_close(control
);
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
);
406 size_t num_inputs
= 0;
407 size_t num_outputs
= 0;
408 if (potential_inputs
) {
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
);
418 if (potential_outputs
) {
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
;
428 for (size_t i
= 0; i
< potential_inputs
; i
++) {
429 snd_rawmidi_info_t
*info
= in_info_list
.at(i
);
431 input_ports
[num_inputs
] = new JackALSARawMidiInputPort(info
, i
);
433 } catch (std::exception e
) {
434 jack_error("JackALSARawMidiDriver::Open - while creating new "
435 "JackALSARawMidiInputPort: %s", e
.what());
437 snd_rawmidi_info_free(info
);
439 for (size_t i
= 0; i
< potential_outputs
; i
++) {
440 snd_rawmidi_info_t
*info
= out_info_list
.at(i
);
442 output_ports
[num_outputs
] = new JackALSARawMidiOutputPort(info
, i
);
444 } catch (std::exception e
) {
445 jack_error("JackALSARawMidiDriver::Open - while creating new "
446 "JackALSARawMidiOutputPort: %s", e
.what());
448 snd_rawmidi_info_free(info
);
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
,
457 jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error");
462 for (size_t i
= 0; i
< num_outputs
; i
++) {
463 delete output_ports
[i
];
465 delete[] output_ports
;
470 for (size_t i
= 0; i
< num_inputs
; i
++) {
471 delete input_ports
[i
];
473 delete[] input_ports
;
480 JackALSARawMidiDriver::Read()
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
)) {
492 JackALSARawMidiDriver::Start()
495 jack_info("JackALSARawMidiDriver::Start - Starting 'alsarawmidi' driver.");
497 JackMidiDriver::Start();
499 for (int i
= 0; i
< fCaptureChannels
; i
++) {
500 poll_fd_count
+= input_ports
[i
]->GetPollDescriptorCount();
502 for (int i
= 0; i
< fPlaybackChannels
; i
++) {
503 poll_fd_count
+= output_ports
[i
]->GetPollDescriptorCount();
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());
512 if (fPlaybackChannels
) {
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
;
521 struct pollfd
*poll_fd_iter
;
523 CreateNonBlockingPipe(fds
);
524 } catch (std::exception e
) {
525 jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: "
527 goto free_output_port_timeouts
;
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();
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;
544 jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ...");
546 if (! thread
->StartSync()) {
548 jack_info("JackALSARawMidiDriver::Start - started ALSA thread.");
552 jack_error("JackALSARawMidiDriver::Start - failed to start MIDI "
553 "processing thread.");
555 DestroyNonBlockingPipe(fds
);
558 free_output_port_timeouts
:
559 delete[] output_port_timeouts
;
560 output_port_timeouts
= 0;
561 free_poll_descriptors
:
568 JackALSARawMidiDriver::Stop()
570 jack_info("JackALSARawMidiDriver::Stop - stopping 'alsarawmidi' driver.");
571 JackMidiDriver::Stop();
579 switch (thread
->GetStatus()) {
580 case JackThread::kIniting
:
581 case JackThread::kStarting
:
582 result
= thread
->Kill();
585 case JackThread::kRunning
:
586 result
= thread
->Stop();
597 if (output_port_timeouts
) {
598 delete[] output_port_timeouts
;
599 output_port_timeouts
= 0;
606 jack_error("JackALSARawMidiDriver::Stop - could not %s MIDI "
607 "processing thread.", verb
);
613 JackALSARawMidiDriver::Write()
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
)) {
628 SERVER_EXPORT jack_driver_desc_t
*
629 driver_get_descriptor()
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.
635 return jack_driver_descriptor_construct("alsarawmidi", JackDriverSlave
, "Alternative ALSA raw MIDI backend.", NULL
);
638 SERVER_EXPORT
Jack::JackDriverClientInterface
*
639 driver_initialize(Jack::JackLockedEngine
*engine
, Jack::JackSynchro
*table
,
640 const JSList
*params
)
642 Jack::JackDriverClientInterface
*driver
=
643 new Jack::JackALSARawMidiDriver("system_midi", "alsarawmidi",
645 if (driver
->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0)) {