Jack2  1.9.10
JackALSARawMidiDriver.cpp
00001 /*
00002 Copyright (C) 2011 Devin Anderson
00003 
00004 This program is free software; you can redistribute it and/or modify
00005 it under the terms of the GNU General Public License as published by
00006 the Free Software Foundation; either version 2 of the License, or
00007 (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 
00018 */
00019 
00020 #include <memory>
00021 #include <new>
00022 #include <stdexcept>
00023 
00024 #include <alsa/asoundlib.h>
00025 
00026 #include "JackALSARawMidiDriver.h"
00027 #include "JackALSARawMidiUtil.h"
00028 #include "JackEngineControl.h"
00029 #include "JackError.h"
00030 #include "JackMidiUtil.h"
00031 #include "driver_interface.h"
00032 
00033 using Jack::JackALSARawMidiDriver;
00034 
00035 JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name,
00036                                              const char *alias,
00037                                              JackLockedEngine *engine,
00038                                              JackSynchro *table):
00039     JackMidiDriver(name, alias, engine, table)
00040 {
00041     thread = new JackThread(this);
00042     fds[0] = -1;
00043     fds[1] = -1;
00044     input_ports = 0;
00045     output_ports = 0;
00046     output_port_timeouts = 0;
00047     poll_fds = 0;
00048 }
00049 
00050 JackALSARawMidiDriver::~JackALSARawMidiDriver()
00051 {
00052     delete thread;
00053 }
00054 
00055 int
00056 JackALSARawMidiDriver::Attach()
00057 {
00058     const char *alias;
00059     jack_nframes_t buffer_size = fEngineControl->fBufferSize;
00060     jack_port_id_t index;
00061     jack_nframes_t latency = buffer_size;
00062     jack_latency_range_t latency_range;
00063     const char *name;
00064     JackPort *port;
00065     latency_range.max = latency;
00066     latency_range.min = latency;
00067     for (int i = 0; i < fCaptureChannels; i++) {
00068         JackALSARawMidiInputPort *input_port = input_ports[i];
00069         name = input_port->GetName();
00070         fEngine->PortRegister(fClientControl.fRefNum, name,
00071                             JACK_DEFAULT_MIDI_TYPE,
00072                             CaptureDriverFlags, buffer_size, &index);
00073         if (index == NO_PORT) {
00074             jack_error("JackALSARawMidiDriver::Attach - cannot register input "
00075                        "port with name '%s'.", name);
00076             // XX: Do we need to deallocate ports?
00077             return -1;
00078         }
00079         alias = input_port->GetAlias();
00080         port = fGraphManager->GetPort(index);
00081         port->SetAlias(alias);
00082         port->SetLatencyRange(JackCaptureLatency, &latency_range);
00083         fCapturePortList[i] = index;
00084 
00085         jack_info("JackALSARawMidiDriver::Attach - input port registered "
00086                   "(name='%s', alias='%s').", name, alias);
00087     }
00088     if (! fEngineControl->fSyncMode) {
00089         latency += buffer_size;
00090         latency_range.max = latency;
00091         latency_range.min = latency;
00092     }
00093     for (int i = 0; i < fPlaybackChannels; i++) {
00094         JackALSARawMidiOutputPort *output_port = output_ports[i];
00095         name = output_port->GetName();
00096         fEngine->PortRegister(fClientControl.fRefNum, name,
00097                             JACK_DEFAULT_MIDI_TYPE,
00098                             PlaybackDriverFlags, buffer_size, &index);
00099         if (index == NO_PORT) {
00100             jack_error("JackALSARawMidiDriver::Attach - cannot register "
00101                        "output port with name '%s'.", name);
00102             // XX: Do we need to deallocate ports?
00103             return -1;
00104         }
00105         alias = output_port->GetAlias();
00106         port = fGraphManager->GetPort(index);
00107         port->SetAlias(alias);
00108         port->SetLatencyRange(JackPlaybackLatency, &latency_range);
00109         fPlaybackPortList[i] = index;
00110 
00111         jack_info("JackALSARawMidiDriver::Attach - output port registered "
00112                   "(name='%s', alias='%s').", name, alias);
00113     }
00114     return 0;
00115 }
00116 
00117 int
00118 JackALSARawMidiDriver::Close()
00119 {
00120     // Generic MIDI driver close
00121     int result = JackMidiDriver::Close();
00122 
00123     if (input_ports) {
00124         for (int i = 0; i < fCaptureChannels; i++) {
00125             delete input_ports[i];
00126         }
00127         delete[] input_ports;
00128         input_ports = 0;
00129     }
00130     if (output_ports) {
00131         for (int i = 0; i < fPlaybackChannels; i++) {
00132             delete output_ports[i];
00133         }
00134         delete[] output_ports;
00135         output_ports = 0;
00136     }
00137     return result;
00138 }
00139 
00140 bool
00141 JackALSARawMidiDriver::Execute()
00142 {
00143     jack_nframes_t timeout_frame = 0;
00144     for (;;) {
00145         struct timespec timeout;
00146         struct timespec *timeout_ptr;
00147         if (! timeout_frame) {
00148             timeout_ptr = 0;
00149         } else {
00150 
00151             // The timeout value is relative to the time that
00152             // 'GetMicroSeconds()' is called, not the time that 'poll()' is
00153             // called.  This means that the amount of time that passes between
00154             // 'GetMicroSeconds()' and 'ppoll()' is time that will be lost
00155             // while waiting for 'poll() to timeout.
00156             //
00157             // I tried to replace the timeout with a 'timerfd' with absolute
00158             // times, but, strangely, it actually slowed things down, and made
00159             // the code a lot more complicated.
00160             //
00161             // I wonder about using the 'epoll' interface instead of 'ppoll()'.
00162             // The problem with the 'epoll' interface is that the timeout
00163             // resolution of 'epoll_wait()' is set in milliseconds.  We need
00164             // microsecond resolution.  Without microsecond resolution, we
00165             // impose the same jitter as USB MIDI.
00166             //
00167             // Another problem is that 'ppoll()' returns later than the wait
00168             // time.  The problem can be minimized with high precision timers.
00169 
00170             timeout_ptr = &timeout;
00171             jack_time_t next_time = GetTimeFromFrames(timeout_frame);
00172             jack_time_t now = GetMicroSeconds();
00173             if (next_time <= now) {
00174                 timeout.tv_sec = 0;
00175                 timeout.tv_nsec = 0;
00176             } else {
00177                 jack_time_t wait_time = next_time - now;
00178                 timeout.tv_sec = wait_time / 1000000;
00179                 timeout.tv_nsec = (wait_time % 1000000) * 1000;
00180             }
00181         }
00182         int poll_result = ppoll(poll_fds, poll_fd_count, timeout_ptr, 0);
00183 
00184         // Getting the current frame value here allows us to use it for
00185         // incoming MIDI bytes.  This makes sense, as the data has already
00186         // arrived at this point.
00187         jack_nframes_t current_frame = GetCurrentFrame();
00188 
00189         if (poll_result == -1) {
00190             if (errno == EINTR) {
00191                 continue;
00192             }
00193             jack_error("JackALSARawMidiDriver::Execute - poll error: %s",
00194                        strerror(errno));
00195             break;
00196         }
00197         jack_nframes_t port_timeout;
00198         timeout_frame = 0;
00199         if (! poll_result) {
00200 
00201             // No I/O events occurred.  So, only handle timeout events on
00202             // output ports.
00203 
00204             for (int i = 0; i < fPlaybackChannels; i++) {
00205                 port_timeout = output_port_timeouts[i];
00206                 if (port_timeout && (port_timeout <= current_frame)) {
00207                     if (! output_ports[i]->ProcessPollEvents(false, true,
00208                                                              &port_timeout)) {
00209                         jack_error("JackALSARawMidiDriver::Execute - a fatal "
00210                                    "error occurred while processing ALSA "
00211                                    "output events.");
00212                         goto cleanup;
00213                     }
00214                     output_port_timeouts[i] = port_timeout;
00215                 }
00216                 if (port_timeout && ((! timeout_frame) ||
00217                                      (port_timeout < timeout_frame))) {
00218                     timeout_frame = port_timeout;
00219                 }
00220             }
00221             continue;
00222         }
00223 
00224         // See if it's time to shutdown.
00225 
00226         unsigned short revents = poll_fds[0].revents;
00227         if (revents) {
00228             if (revents & (~ POLLHUP)) {
00229                 jack_error("JackALSARawMidiDriver::Execute - unexpected poll "
00230                            "event on pipe file descriptor.");
00231             }
00232             break;
00233         }
00234 
00235         // Handle I/O events *and* timeout events on output ports.
00236 
00237         for (int i = 0; i < fPlaybackChannels; i++) {
00238             port_timeout = output_port_timeouts[i];
00239             bool timeout = port_timeout && (port_timeout <= current_frame);
00240             if (! output_ports[i]->ProcessPollEvents(true, timeout,
00241                                                      &port_timeout)) {
00242                 jack_error("JackALSARawMidiDriver::Execute - a fatal error "
00243                            "occurred while processing ALSA output events.");
00244                 goto cleanup;
00245             }
00246             output_port_timeouts[i] = port_timeout;
00247             if (port_timeout && ((! timeout_frame) ||
00248                                  (port_timeout < timeout_frame))) {
00249                 timeout_frame = port_timeout;
00250             }
00251         }
00252 
00253         // Handle I/O events on input ports.  We handle these last because we
00254         // already computed the arrival time above, and will impose a delay on
00255         // the events by 'period-size' frames anyway, which gives us a bit of
00256         // borrowed time.
00257 
00258         for (int i = 0; i < fCaptureChannels; i++) {
00259             if (! input_ports[i]->ProcessPollEvents(current_frame)) {
00260                 jack_error("JackALSARawMidiDriver::Execute - a fatal error "
00261                            "occurred while processing ALSA input events.");
00262                 goto cleanup;
00263             }
00264         }
00265     }
00266  cleanup:
00267     close(fds[0]);
00268     fds[0] = -1;
00269 
00270     jack_info("JackALSARawMidiDriver::Execute - ALSA thread exiting.");
00271 
00272     return false;
00273 }
00274 
00275 void
00276 JackALSARawMidiDriver::
00277 FreeDeviceInfo(std::vector<snd_rawmidi_info_t *> *in_info_list,
00278                std::vector<snd_rawmidi_info_t *> *out_info_list)
00279 {
00280     size_t length = in_info_list->size();
00281     for (size_t i = 0; i < length; i++) {
00282         snd_rawmidi_info_free(in_info_list->at(i));
00283     }
00284     length = out_info_list->size();
00285     for (size_t i = 0; i < length; i++) {
00286         snd_rawmidi_info_free(out_info_list->at(i));
00287     }
00288 }
00289 
00290 void
00291 JackALSARawMidiDriver::
00292 GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info,
00293               std::vector<snd_rawmidi_info_t *> *info_list)
00294 {
00295     snd_rawmidi_info_set_subdevice(info, 0);
00296     int code = snd_ctl_rawmidi_info(control, info);
00297     if (code) {
00298         if (code != -ENOENT) {
00299             HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
00300         }
00301         return;
00302     }
00303     unsigned int count = snd_rawmidi_info_get_subdevices_count(info);
00304     for (unsigned int i = 0; i < count; i++) {
00305         snd_rawmidi_info_set_subdevice(info, i);
00306         int code = snd_ctl_rawmidi_info(control, info);
00307         if (code) {
00308             HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
00309             continue;
00310         }
00311         snd_rawmidi_info_t *info_copy;
00312         code = snd_rawmidi_info_malloc(&info_copy);
00313         if (code) {
00314             HandleALSAError("GetDeviceInfo", "snd_rawmidi_info_malloc", code);
00315             continue;
00316         }
00317         snd_rawmidi_info_copy(info_copy, info);
00318         try {
00319             info_list->push_back(info_copy);
00320         } catch (std::bad_alloc &e) {
00321             snd_rawmidi_info_free(info_copy);
00322             jack_error("JackALSARawMidiDriver::GetDeviceInfo - "
00323                        "std::vector::push_back: %s", e.what());
00324         }
00325     }
00326 }
00327 
00328 void
00329 JackALSARawMidiDriver::HandleALSAError(const char *driver_func,
00330                                        const char *alsa_func, int code)
00331 {
00332     jack_error("JackALSARawMidiDriver::%s - %s: %s", driver_func, alsa_func,
00333                snd_strerror(code));
00334 }
00335 
00336 bool
00337 JackALSARawMidiDriver::Init()
00338 {
00339     set_threaded_log_function();
00340     if (thread->AcquireSelfRealTime(fEngineControl->fServerPriority + 1)) {
00341         jack_error("JackALSARawMidiDriver::Init - could not acquire realtime "
00342                    "scheduling.  Continuing anyway.");
00343     }
00344     return true;
00345 }
00346 
00347 int
00348 JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels,
00349                             int out_channels, bool monitor,
00350                             const char *capture_driver_name,
00351                             const char *playback_driver_name,
00352                             jack_nframes_t capture_latency,
00353                             jack_nframes_t playback_latency)
00354 {
00355     snd_rawmidi_info_t *info;
00356     int code = snd_rawmidi_info_malloc(&info);
00357     if (code) {
00358         HandleALSAError("Open", "snd_rawmidi_info_malloc", code);
00359         return -1;
00360     }
00361     std::vector<snd_rawmidi_info_t *> in_info_list;
00362     std::vector<snd_rawmidi_info_t *> out_info_list;
00363     for (int card = -1;;) {
00364         int code = snd_card_next(&card);
00365         if (code) {
00366             HandleALSAError("Open", "snd_card_next", code);
00367             continue;
00368         }
00369         if (card == -1) {
00370             break;
00371         }
00372         char name[32];
00373         snprintf(name, sizeof(name), "hw:%d", card);
00374         snd_ctl_t *control;
00375         code = snd_ctl_open(&control, name, SND_CTL_NONBLOCK);
00376         if (code) {
00377             HandleALSAError("Open", "snd_ctl_open", code);
00378             continue;
00379         }
00380         for (int device = -1;;) {
00381             code = snd_ctl_rawmidi_next_device(control, &device);
00382             if (code) {
00383                 HandleALSAError("Open", "snd_ctl_rawmidi_next_device", code);
00384                 continue;
00385             }
00386             if (device == -1) {
00387                 break;
00388             }
00389             snd_rawmidi_info_set_device(info, device);
00390             snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
00391             GetDeviceInfo(control, info, &in_info_list);
00392             snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT);
00393             GetDeviceInfo(control, info, &out_info_list);
00394         }
00395         snd_ctl_close(control);
00396     }
00397     snd_rawmidi_info_free(info);
00398     size_t potential_inputs = in_info_list.size();
00399     size_t potential_outputs = out_info_list.size();
00400     if (! (potential_inputs || potential_outputs)) {
00401         jack_error("JackALSARawMidiDriver::Open - no ALSA raw MIDI input or "
00402                    "output ports found.");
00403         FreeDeviceInfo(&in_info_list, &out_info_list);
00404         return -1;
00405     }
00406     size_t num_inputs = 0;
00407     size_t num_outputs = 0;
00408     if (potential_inputs) {
00409         try {
00410             input_ports = new JackALSARawMidiInputPort *[potential_inputs];
00411         } catch (std::exception e) {
00412             jack_error("JackALSARawMidiDriver::Open - while creating input "
00413                        "port array: %s", e.what());
00414             FreeDeviceInfo(&in_info_list, &out_info_list);
00415             return -1;
00416         }
00417     }
00418     if (potential_outputs) {
00419         try {
00420             output_ports = new JackALSARawMidiOutputPort *[potential_outputs];
00421         } catch (std::exception e) {
00422             jack_error("JackALSARawMidiDriver::Open - while creating output "
00423                        "port array: %s", e.what());
00424             FreeDeviceInfo(&in_info_list, &out_info_list);
00425             goto delete_input_ports;
00426         }
00427     }
00428     for (size_t i = 0; i < potential_inputs; i++) {
00429         snd_rawmidi_info_t *info = in_info_list.at(i);
00430         try {
00431             input_ports[num_inputs] = new JackALSARawMidiInputPort(info, i);
00432             num_inputs++;
00433         } catch (std::exception e) {
00434             jack_error("JackALSARawMidiDriver::Open - while creating new "
00435                        "JackALSARawMidiInputPort: %s", e.what());
00436         }
00437         snd_rawmidi_info_free(info);
00438     }
00439     for (size_t i = 0; i < potential_outputs; i++) {
00440         snd_rawmidi_info_t *info = out_info_list.at(i);
00441         try {
00442             output_ports[num_outputs] = new JackALSARawMidiOutputPort(info, i);
00443             num_outputs++;
00444         } catch (std::exception e) {
00445             jack_error("JackALSARawMidiDriver::Open - while creating new "
00446                        "JackALSARawMidiOutputPort: %s", e.what());
00447         }
00448         snd_rawmidi_info_free(info);
00449     }
00450     if (! (num_inputs || num_outputs)) {
00451         jack_error("JackALSARawMidiDriver::Open - none of the potential "
00452                    "inputs or outputs were successfully opened.");
00453     } else if (JackMidiDriver::Open(capturing, playing, num_inputs,
00454                                     num_outputs, monitor, capture_driver_name,
00455                                     playback_driver_name, capture_latency,
00456                                     playback_latency)) {
00457         jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error");
00458     } else {
00459         return 0;
00460     }
00461     if (output_ports) {
00462         for (size_t i = 0; i < num_outputs; i++) {
00463             delete output_ports[i];
00464         }
00465         delete[] output_ports;
00466         output_ports = 0;
00467     }
00468  delete_input_ports:
00469     if (input_ports) {
00470         for (size_t i = 0; i < num_inputs; i++) {
00471             delete input_ports[i];
00472         }
00473         delete[] input_ports;
00474         input_ports = 0;
00475     }
00476     return -1;
00477 }
00478 
00479 int
00480 JackALSARawMidiDriver::Read()
00481 {
00482     jack_nframes_t buffer_size = fEngineControl->fBufferSize;
00483     for (int i = 0; i < fCaptureChannels; i++) {
00484         if (! input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size)) {
00485             return -1;
00486         }
00487     }
00488     return 0;
00489 }
00490 
00491 int
00492 JackALSARawMidiDriver::Start()
00493 {
00494 
00495     jack_info("JackALSARawMidiDriver::Start - Starting 'alsarawmidi' driver.");
00496 
00497     JackMidiDriver::Start();
00498     poll_fd_count = 1;
00499     for (int i = 0; i < fCaptureChannels; i++) {
00500         poll_fd_count += input_ports[i]->GetPollDescriptorCount();
00501     }
00502     for (int i = 0; i < fPlaybackChannels; i++) {
00503         poll_fd_count += output_ports[i]->GetPollDescriptorCount();
00504     }
00505     try {
00506         poll_fds = new pollfd[poll_fd_count];
00507     } catch (std::exception e) {
00508         jack_error("JackALSARawMidiDriver::Start - creating poll descriptor "
00509                    "structures failed: %s", e.what());
00510         return -1;
00511     }
00512     if (fPlaybackChannels) {
00513         try {
00514             output_port_timeouts = new jack_nframes_t[fPlaybackChannels];
00515         } catch (std::exception e) {
00516             jack_error("JackALSARawMidiDriver::Start - creating array for "
00517                        "output port timeout values failed: %s", e.what());
00518             goto free_poll_descriptors;
00519         }
00520     }
00521     struct pollfd *poll_fd_iter;
00522     try {
00523         CreateNonBlockingPipe(fds);
00524     } catch (std::exception e) {
00525         jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: "
00526                    "%s", e.what());
00527         goto free_output_port_timeouts;
00528     }
00529     poll_fds[0].events = POLLERR | POLLIN | POLLNVAL;
00530     poll_fds[0].fd = fds[0];
00531     poll_fd_iter = poll_fds + 1;
00532     for (int i = 0; i < fCaptureChannels; i++) {
00533         JackALSARawMidiInputPort *input_port = input_ports[i];
00534         input_port->PopulatePollDescriptors(poll_fd_iter);
00535         poll_fd_iter += input_port->GetPollDescriptorCount();
00536     }
00537     for (int i = 0; i < fPlaybackChannels; i++) {
00538         JackALSARawMidiOutputPort *output_port = output_ports[i];
00539         output_port->PopulatePollDescriptors(poll_fd_iter);
00540         poll_fd_iter += output_port->GetPollDescriptorCount();
00541         output_port_timeouts[i] = 0;
00542     }
00543 
00544     jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ...");
00545 
00546     if (! thread->StartSync()) {
00547 
00548         jack_info("JackALSARawMidiDriver::Start - started ALSA thread.");
00549 
00550         return 0;
00551     }
00552     jack_error("JackALSARawMidiDriver::Start - failed to start MIDI "
00553                "processing thread.");
00554 
00555     DestroyNonBlockingPipe(fds);
00556     fds[1] = -1;
00557     fds[0] = -1;
00558  free_output_port_timeouts:
00559     delete[] output_port_timeouts;
00560     output_port_timeouts = 0;
00561  free_poll_descriptors:
00562     delete[] poll_fds;
00563     poll_fds = 0;
00564     return -1;
00565 }
00566 
00567 int
00568 JackALSARawMidiDriver::Stop()
00569 {
00570     jack_info("JackALSARawMidiDriver::Stop - stopping 'alsarawmidi' driver.");
00571     JackMidiDriver::Stop();
00572 
00573     if (fds[1] != -1) {
00574         close(fds[1]);
00575         fds[1] = -1;
00576     }
00577     int result;
00578     const char *verb;
00579     switch (thread->GetStatus()) {
00580     case JackThread::kIniting:
00581     case JackThread::kStarting:
00582         result = thread->Kill();
00583         verb = "kill";
00584         break;
00585     case JackThread::kRunning:
00586         result = thread->Stop();
00587         verb = "stop";
00588         break;
00589     default:
00590         result = 0;
00591         verb = 0;
00592     }
00593     if (fds[0] != -1) {
00594         close(fds[0]);
00595         fds[0] = -1;
00596     }
00597     if (output_port_timeouts) {
00598         delete[] output_port_timeouts;
00599         output_port_timeouts = 0;
00600     }
00601     if (poll_fds) {
00602         delete[] poll_fds;
00603         poll_fds = 0;
00604     }
00605     if (result) {
00606         jack_error("JackALSARawMidiDriver::Stop - could not %s MIDI "
00607                    "processing thread.", verb);
00608     }
00609     return result;
00610 }
00611 
00612 int
00613 JackALSARawMidiDriver::Write()
00614 {
00615     jack_nframes_t buffer_size = fEngineControl->fBufferSize;
00616     for (int i = 0; i < fPlaybackChannels; i++) {
00617         if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size)) {
00618             return -1;
00619         }
00620     }
00621     return 0;
00622 }
00623 
00624 #ifdef __cplusplus
00625 extern "C" {
00626 #endif
00627     
00628     // singleton kind of driver
00629     static Jack::JackALSARawMidiDriver* driver = NULL;
00630 
00631     SERVER_EXPORT jack_driver_desc_t *
00632     driver_get_descriptor()
00633     {
00634         // X: There could be parameters here regarding setting I/O buffer
00635         // sizes.  I don't think MIDI drivers can accept parameters right
00636         // now without being set as the main driver.
00637 
00638         return jack_driver_descriptor_construct("alsarawmidi", JackDriverSlave, "Alternative ALSA raw MIDI backend.", NULL);
00639     }
00640 
00641     SERVER_EXPORT Jack::JackDriverClientInterface *
00642     driver_initialize(Jack::JackLockedEngine *engine, Jack::JackSynchro *table,
00643                       const JSList *params)
00644     {
00645         // singleton kind of driver
00646         if (!driver) {
00647             driver = new Jack::JackALSARawMidiDriver("system_midi", "alsarawmidi", engine, table);
00648             if (driver->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0) == 0) {
00649                 return driver;
00650             } else {
00651                 delete driver;
00652                 return NULL;
00653             }
00654         } else {
00655             jack_info("JackALSARawMidiDriver already allocated, cannot be loaded twice");
00656             return NULL;
00657         }
00658     }
00659 
00660 #ifdef __cplusplus
00661 }
00662 #endif