Jack2 1.9.10
|
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