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