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