jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

686 lines
24KB

  1. /*
  2. Copyright (C) 2009 Grame
  3. Copyright (C) 2011 Devin Anderson
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #include <stdexcept>
  17. #include <mach/mach_time.h>
  18. #include "JackCoreMidiDriver.h"
  19. #include "JackCoreMidiUtil.h"
  20. #include "JackEngineControl.h"
  21. using Jack::JackCoreMidiDriver;
  22. ///////////////////////////////////////////////////////////////////////////////
  23. // Static callbacks
  24. ///////////////////////////////////////////////////////////////////////////////
  25. void
  26. JackCoreMidiDriver::HandleInputEvent(const MIDIPacketList *packet_list,
  27. void *driver, void *port)
  28. {
  29. ((JackCoreMidiPhysicalInputPort *) port)->ProcessCoreMidi(packet_list);
  30. }
  31. void
  32. JackCoreMidiDriver::HandleNotificationEvent(const MIDINotification *message,
  33. void *driver)
  34. {
  35. ((JackCoreMidiDriver *) driver)->HandleNotification(message);
  36. }
  37. ///////////////////////////////////////////////////////////////////////////////
  38. // Class
  39. ///////////////////////////////////////////////////////////////////////////////
  40. JackCoreMidiDriver::JackCoreMidiDriver(const char *name, const char *alias,
  41. JackLockedEngine *engine,
  42. JackSynchro *table):
  43. JackMidiDriver(name, alias, engine, table)
  44. {
  45. mach_timebase_info_data_t info;
  46. kern_return_t result = mach_timebase_info(&info);
  47. if (result != KERN_SUCCESS) {
  48. throw std::runtime_error(mach_error_string(result));
  49. }
  50. client = 0;
  51. fCaptureChannels = 0;
  52. fPlaybackChannels = 0;
  53. num_physical_inputs = 0;
  54. num_physical_outputs = 0;
  55. num_virtual_inputs = 0;
  56. num_virtual_outputs = 0;
  57. physical_input_ports = 0;
  58. physical_output_ports = 0;
  59. time_ratio = (((double) info.numer) / info.denom) / 1000.0;
  60. virtual_input_ports = 0;
  61. virtual_output_ports = 0;
  62. }
  63. JackCoreMidiDriver::~JackCoreMidiDriver()
  64. {}
  65. int
  66. JackCoreMidiDriver::Attach()
  67. {
  68. jack_nframes_t buffer_size = fEngineControl->fBufferSize;
  69. jack_port_id_t index;
  70. jack_nframes_t latency = buffer_size;
  71. jack_latency_range_t latency_range;
  72. const char *name;
  73. JackPort *port;
  74. JackCoreMidiPort *port_obj;
  75. latency_range.max = latency;
  76. latency_range.min = latency;
  77. // Physical inputs
  78. for (int i = 0; i < num_physical_inputs; i++) {
  79. port_obj = physical_input_ports[i];
  80. name = port_obj->GetName();
  81. index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
  82. JACK_DEFAULT_MIDI_TYPE,
  83. CaptureDriverFlags, buffer_size);
  84. if (index == NO_PORT) {
  85. jack_error("JackCoreMidiDriver::Attach - cannot register physical "
  86. "input port with name '%s'.", name);
  87. // X: Do we need to deallocate ports?
  88. return -1;
  89. }
  90. port = fGraphManager->GetPort(index);
  91. port->SetAlias(port_obj->GetAlias());
  92. port->SetLatencyRange(JackCaptureLatency, &latency_range);
  93. fCapturePortList[i] = index;
  94. }
  95. // Virtual inputs
  96. for (int i = 0; i < num_virtual_inputs; i++) {
  97. port_obj = virtual_input_ports[i];
  98. name = port_obj->GetName();
  99. index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
  100. JACK_DEFAULT_MIDI_TYPE,
  101. CaptureDriverFlags, buffer_size);
  102. if (index == NO_PORT) {
  103. jack_error("JackCoreMidiDriver::Attach - cannot register virtual "
  104. "input port with name '%s'.", name);
  105. // X: Do we need to deallocate ports?
  106. return -1;
  107. }
  108. port = fGraphManager->GetPort(index);
  109. port->SetAlias(port_obj->GetAlias());
  110. port->SetLatencyRange(JackCaptureLatency, &latency_range);
  111. fCapturePortList[num_physical_inputs + i] = index;
  112. }
  113. if (! fEngineControl->fSyncMode) {
  114. latency += buffer_size;
  115. latency_range.max = latency;
  116. latency_range.min = latency;
  117. }
  118. // Physical outputs
  119. for (int i = 0; i < num_physical_outputs; i++) {
  120. port_obj = physical_output_ports[i];
  121. name = port_obj->GetName();
  122. index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
  123. JACK_DEFAULT_MIDI_TYPE,
  124. PlaybackDriverFlags, buffer_size);
  125. if (index == NO_PORT) {
  126. jack_error("JackCoreMidiDriver::Attach - cannot register physical "
  127. "output port with name '%s'.", name);
  128. // X: Do we need to deallocate ports?
  129. return -1;
  130. }
  131. port = fGraphManager->GetPort(index);
  132. port->SetAlias(port_obj->GetAlias());
  133. port->SetLatencyRange(JackPlaybackLatency, &latency_range);
  134. fPlaybackPortList[i] = index;
  135. }
  136. // Virtual outputs
  137. for (int i = 0; i < num_virtual_outputs; i++) {
  138. port_obj = virtual_output_ports[i];
  139. name = port_obj->GetName();
  140. index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
  141. JACK_DEFAULT_MIDI_TYPE,
  142. PlaybackDriverFlags, buffer_size);
  143. if (index == NO_PORT) {
  144. jack_error("JackCoreMidiDriver::Attach - cannot register virtual "
  145. "output port with name '%s'.", name);
  146. // X: Do we need to deallocate ports?
  147. return -1;
  148. }
  149. port = fGraphManager->GetPort(index);
  150. port->SetAlias(port_obj->GetAlias());
  151. port->SetLatencyRange(JackPlaybackLatency, &latency_range);
  152. fPlaybackPortList[num_physical_outputs + i] = index;
  153. }
  154. return 0;
  155. }
  156. int
  157. JackCoreMidiDriver::Close()
  158. {
  159. // Generic MIDI driver close
  160. int result = JackMidiDriver::Close();
  161. OSStatus status;
  162. if (physical_input_ports) {
  163. for (int i = 0; i < num_physical_inputs; i++) {
  164. delete physical_input_ports[i];
  165. }
  166. delete[] physical_input_ports;
  167. num_physical_inputs = 0;
  168. physical_input_ports = 0;
  169. status = MIDIPortDispose(internal_input);
  170. if (status != noErr) {
  171. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose",
  172. status);
  173. result = -1;
  174. }
  175. }
  176. if (physical_output_ports) {
  177. for (int i = 0; i < num_physical_outputs; i++) {
  178. delete physical_output_ports[i];
  179. }
  180. delete[] physical_output_ports;
  181. num_physical_outputs = 0;
  182. physical_output_ports = 0;
  183. status = MIDIPortDispose(internal_output);
  184. if (status != noErr) {
  185. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose",
  186. status);
  187. result = -1;
  188. }
  189. }
  190. if (virtual_input_ports) {
  191. for (int i = 0; i < num_virtual_inputs; i++) {
  192. delete virtual_input_ports[i];
  193. }
  194. delete[] virtual_input_ports;
  195. num_virtual_inputs = 0;
  196. virtual_input_ports = 0;
  197. }
  198. if (virtual_output_ports) {
  199. for (int i = 0; i < num_virtual_outputs; i++) {
  200. delete virtual_output_ports[i];
  201. }
  202. delete[] virtual_output_ports;
  203. num_virtual_outputs = 0;
  204. virtual_output_ports = 0;
  205. }
  206. if (client) {
  207. status = MIDIClientDispose(client);
  208. if (status != noErr) {
  209. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientDispose",
  210. status);
  211. result = -1;
  212. }
  213. client = 0;
  214. }
  215. return result;
  216. }
  217. void
  218. JackCoreMidiDriver::HandleNotification(const MIDINotification *message)
  219. {
  220. // Empty
  221. }
  222. int
  223. JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels,
  224. int out_channels, bool monitor,
  225. const char* capture_driver_name,
  226. const char* playback_driver_name,
  227. jack_nframes_t capture_latency,
  228. jack_nframes_t playback_latency)
  229. {
  230. int pi_count = 0;
  231. int po_count = 0;
  232. int vi_count = 0;
  233. int vo_count = 0;
  234. ItemCount potential_po_count;
  235. ItemCount potential_pi_count;
  236. CFStringRef name = CFStringCreateWithCString(0, "JackMidi",
  237. CFStringGetSystemEncoding());
  238. if (! name) {
  239. jack_error("JackCoreMidiDriver::Open - failed to allocate memory for "
  240. "client name string");
  241. return -1;
  242. }
  243. OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this,
  244. &client);
  245. CFRelease(name);
  246. if (status != noErr) {
  247. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientCreate",
  248. status);
  249. return -1;
  250. }
  251. char *client_name = fClientControl.fName;
  252. // Allocate and connect physical inputs
  253. potential_pi_count = MIDIGetNumberOfSources();
  254. if (potential_pi_count) {
  255. status = MIDIInputPortCreate(client, CFSTR("Physical Input Port"),
  256. HandleInputEvent, this, &internal_input);
  257. if (status != noErr) {
  258. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIInputPortCreate",
  259. status);
  260. goto destroy_virtual_output_ports;
  261. }
  262. try {
  263. physical_input_ports =
  264. new JackCoreMidiPhysicalInputPort*[potential_pi_count];
  265. } catch (std::exception e) {
  266. jack_error("JackCoreMidiDriver::Open - while creating physical "
  267. "input port array: %s", e.what());
  268. goto destroy_internal_input_port;
  269. }
  270. for (ItemCount i = 0; i < potential_pi_count; i++) {
  271. try {
  272. physical_input_ports[pi_count] =
  273. new JackCoreMidiPhysicalInputPort(fAliasName, client_name,
  274. capture_driver_name, i,
  275. client, internal_input,
  276. time_ratio);
  277. } catch (std::exception e) {
  278. jack_error("JackCoreMidiDriver::Open - while creating "
  279. "physical input port: %s", e.what());
  280. goto destroy_internal_input_port;
  281. }
  282. pi_count++;
  283. }
  284. }
  285. // Allocate and connect physical outputs
  286. potential_po_count = MIDIGetNumberOfDestinations();
  287. if (potential_po_count) {
  288. status = MIDIOutputPortCreate(client, CFSTR("Physical Output Port"),
  289. &internal_output);
  290. if (status != noErr) {
  291. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIOutputPortCreate",
  292. status);
  293. goto destroy_physical_input_ports;
  294. }
  295. try {
  296. physical_output_ports =
  297. new JackCoreMidiPhysicalOutputPort*[potential_po_count];
  298. } catch (std::exception e) {
  299. jack_error("JackCoreMidiDriver::Open - while creating physical "
  300. "output port array: %s", e.what());
  301. goto destroy_internal_output_port;
  302. }
  303. for (ItemCount i = 0; i < potential_po_count; i++) {
  304. try {
  305. physical_output_ports[po_count] =
  306. new JackCoreMidiPhysicalOutputPort(fAliasName, client_name,
  307. playback_driver_name, i,
  308. client, internal_output,
  309. time_ratio);
  310. } catch (std::exception e) {
  311. jack_error("JackCoreMidiDriver::Open - while creating "
  312. "physical output port: %s", e.what());
  313. goto destroy_internal_output_port;
  314. }
  315. po_count++;
  316. }
  317. }
  318. // Allocate and connect virtual inputs
  319. if (in_channels) {
  320. try {
  321. virtual_input_ports =
  322. new JackCoreMidiVirtualInputPort*[in_channels];
  323. } catch (std::exception e) {
  324. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  325. "input port array: %s", e.what());
  326. goto destroy_client;
  327. }
  328. for (vi_count = 0; vi_count < in_channels; vi_count++) {
  329. try {
  330. virtual_input_ports[vi_count] =
  331. new JackCoreMidiVirtualInputPort(fAliasName, client_name,
  332. capture_driver_name,
  333. vi_count + pi_count, client,
  334. time_ratio);
  335. } catch (std::exception e) {
  336. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  337. "input port: %s", e.what());
  338. goto destroy_virtual_input_ports;
  339. }
  340. }
  341. }
  342. // Allocate and connect virtual outputs
  343. if (out_channels) {
  344. try {
  345. virtual_output_ports =
  346. new JackCoreMidiVirtualOutputPort*[out_channels];
  347. } catch (std::exception e) {
  348. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  349. "output port array: %s", e.what());
  350. goto destroy_virtual_input_ports;
  351. }
  352. for (vo_count = 0; vo_count < out_channels; vo_count++) {
  353. try {
  354. virtual_output_ports[vo_count] =
  355. new JackCoreMidiVirtualOutputPort(fAliasName, client_name,
  356. playback_driver_name,
  357. vo_count + po_count, client,
  358. time_ratio);
  359. } catch (std::exception e) {
  360. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  361. "output port: %s", e.what());
  362. goto destroy_virtual_output_ports;
  363. }
  364. }
  365. }
  366. if (! (pi_count || po_count || in_channels || out_channels)) {
  367. jack_error("JackCoreMidiDriver::Open - no CoreMIDI inputs or outputs "
  368. "found, and no virtual ports allocated.");
  369. } else if (! JackMidiDriver::Open(capturing, playing,
  370. in_channels + pi_count,
  371. out_channels + po_count, monitor,
  372. capture_driver_name,
  373. playback_driver_name, capture_latency,
  374. playback_latency)) {
  375. num_physical_inputs = pi_count;
  376. num_physical_outputs = po_count;
  377. num_virtual_inputs = in_channels;
  378. num_virtual_outputs = out_channels;
  379. return 0;
  380. }
  381. // Cleanup
  382. if (physical_output_ports) {
  383. for (int i = 0; i < po_count; i++) {
  384. delete physical_output_ports[i];
  385. }
  386. delete[] physical_output_ports;
  387. physical_output_ports = 0;
  388. }
  389. destroy_internal_output_port:
  390. status = MIDIPortDispose(internal_output);
  391. if (status != noErr) {
  392. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status);
  393. }
  394. destroy_physical_input_ports:
  395. if (physical_input_ports) {
  396. for (int i = 0; i < pi_count; i++) {
  397. delete physical_input_ports[i];
  398. }
  399. delete[] physical_input_ports;
  400. physical_input_ports = 0;
  401. }
  402. destroy_internal_input_port:
  403. status = MIDIPortDispose(internal_input);
  404. if (status != noErr) {
  405. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status);
  406. }
  407. destroy_virtual_output_ports:
  408. if (virtual_output_ports) {
  409. for (int i = 0; i < vo_count; i++) {
  410. delete virtual_output_ports[i];
  411. }
  412. delete[] virtual_output_ports;
  413. virtual_output_ports = 0;
  414. }
  415. destroy_virtual_input_ports:
  416. if (virtual_input_ports) {
  417. for (int i = 0; i < vi_count; i++) {
  418. delete virtual_input_ports[i];
  419. }
  420. delete[] virtual_input_ports;
  421. virtual_input_ports = 0;
  422. }
  423. destroy_client:
  424. status = MIDIClientDispose(client);
  425. if (status != noErr) {
  426. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientDispose",
  427. status);
  428. }
  429. client = 0;
  430. return -1;
  431. }
  432. int
  433. JackCoreMidiDriver::Read()
  434. {
  435. jack_nframes_t buffer_size = fEngineControl->fBufferSize;
  436. for (int i = 0; i < num_physical_inputs; i++) {
  437. physical_input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size);
  438. }
  439. for (int i = 0; i < num_virtual_inputs; i++) {
  440. virtual_input_ports[i]->
  441. ProcessJack(GetInputBuffer(num_physical_inputs + i), buffer_size);
  442. }
  443. return 0;
  444. }
  445. int
  446. JackCoreMidiDriver::Start()
  447. {
  448. jack_info("JackCoreMidiDriver::Start - Starting driver.");
  449. JackMidiDriver::Start();
  450. int pi_count = 0;
  451. int po_count = 0;
  452. int vi_count = 0;
  453. int vo_count = 0;
  454. jack_info("JackCoreMidiDriver::Start - Enabling physical input ports.");
  455. for (; pi_count < num_physical_inputs; pi_count++) {
  456. if (physical_input_ports[pi_count]->Start() < 0) {
  457. jack_error("JackCoreMidiDriver::Start - Failed to enable physical "
  458. "input port.");
  459. goto stop_physical_input_ports;
  460. }
  461. }
  462. jack_info("JackCoreMidiDriver::Start - Enabling physical output ports.");
  463. for (; po_count < num_physical_outputs; po_count++) {
  464. if (physical_output_ports[po_count]->Start() < 0) {
  465. jack_error("JackCoreMidiDriver::Start - Failed to enable physical "
  466. "output port.");
  467. goto stop_physical_output_ports;
  468. }
  469. }
  470. jack_info("JackCoreMidiDriver::Start - Enabling virtual input ports.");
  471. for (; vi_count < num_virtual_inputs; vi_count++) {
  472. if (virtual_input_ports[vi_count]->Start() < 0) {
  473. jack_error("JackCoreMidiDriver::Start - Failed to enable virtual "
  474. "input port.");
  475. goto stop_virtual_input_ports;
  476. }
  477. }
  478. jack_info("JackCoreMidiDriver::Start - Enabling virtual output ports.");
  479. for (; vo_count < num_virtual_outputs; vo_count++) {
  480. if (virtual_output_ports[vo_count]->Start() < 0) {
  481. jack_error("JackCoreMidiDriver::Start - Failed to enable virtual "
  482. "output port.");
  483. goto stop_virtual_output_ports;
  484. }
  485. }
  486. jack_info("JackCoreMidiDriver::Start - Driver started.");
  487. return 0;
  488. stop_virtual_output_ports:
  489. for (int i = 0; i < vo_count; i++) {
  490. if (virtual_output_ports[i]->Stop() < 0) {
  491. jack_error("JackCoreMidiDriver::Start - Failed to disable virtual "
  492. "output port.");
  493. }
  494. }
  495. stop_virtual_input_ports:
  496. for (int i = 0; i < vi_count; i++) {
  497. if (virtual_input_ports[i]->Stop() < 0) {
  498. jack_error("JackCoreMidiDriver::Start - Failed to disable virtual "
  499. "input port.");
  500. }
  501. }
  502. stop_physical_output_ports:
  503. for (int i = 0; i < po_count; i++) {
  504. if (physical_output_ports[i]->Stop() < 0) {
  505. jack_error("JackCoreMidiDriver::Start - Failed to disable "
  506. "physical output port.");
  507. }
  508. }
  509. stop_physical_input_ports:
  510. for (int i = 0; i < pi_count; i++) {
  511. if (physical_input_ports[i]->Stop() < 0) {
  512. jack_error("JackCoreMidiDriver::Start - Failed to disable "
  513. "physical input port.");
  514. }
  515. }
  516. return -1;
  517. }
  518. int
  519. JackCoreMidiDriver::Stop()
  520. {
  521. int result = 0;
  522. jack_info("JackCoreMidiDriver::Stop - disabling physical input ports.");
  523. for (int i = 0; i < num_physical_inputs; i++) {
  524. if (physical_input_ports[i]->Stop() < 0) {
  525. jack_error("JackCoreMidiDriver::Stop - Failed to disable physical "
  526. "input port.");
  527. result = -1;
  528. }
  529. }
  530. jack_info("JackCoreMidiDriver::Stop - disabling physical output ports.");
  531. for (int i = 0; i < num_physical_outputs; i++) {
  532. if (physical_output_ports[i]->Stop() < 0) {
  533. jack_error("JackCoreMidiDriver::Stop - Failed to disable physical "
  534. "output port.");
  535. result = -1;
  536. }
  537. }
  538. jack_info("JackCoreMidiDriver::Stop - disabling virtual input ports.");
  539. for (int i = 0; i < num_virtual_inputs; i++) {
  540. if (virtual_input_ports[i]->Stop() < 0) {
  541. jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual "
  542. "input port.");
  543. result = -1;
  544. }
  545. }
  546. jack_info("JackCoreMidiDriver::Stop - disabling virtual output ports.");
  547. for (int i = 0; i < num_virtual_outputs; i++) {
  548. if (virtual_output_ports[i]->Stop() < 0) {
  549. jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual "
  550. "output port.");
  551. result = -1;
  552. }
  553. }
  554. return result;
  555. }
  556. int
  557. JackCoreMidiDriver::Write()
  558. {
  559. jack_nframes_t buffer_size = fEngineControl->fBufferSize;
  560. for (int i = 0; i < num_physical_outputs; i++) {
  561. physical_output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size);
  562. }
  563. for (int i = 0; i < num_virtual_outputs; i++) {
  564. virtual_output_ports[i]->
  565. ProcessJack(GetOutputBuffer(num_physical_outputs + i),
  566. buffer_size);
  567. }
  568. return 0;
  569. }
  570. #ifdef __cplusplus
  571. extern "C" {
  572. #endif
  573. SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor()
  574. {
  575. jack_driver_desc_t * desc;
  576. jack_driver_desc_filler_t filler;
  577. jack_driver_param_value_t value;
  578. desc = jack_driver_descriptor_construct("coremidi", "Apple CoreMIDI API based MIDI backend", &filler);
  579. value.ui = 0;
  580. jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL);
  581. jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL);
  582. return desc;
  583. }
  584. SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
  585. {
  586. const JSList * node;
  587. const jack_driver_param_t * param;
  588. int virtual_in = 0;
  589. int virtual_out = 0;
  590. for (node = params; node; node = jack_slist_next (node)) {
  591. param = (const jack_driver_param_t *) node->data;
  592. switch (param->character) {
  593. case 'i':
  594. virtual_in = param->value.ui;
  595. break;
  596. case 'o':
  597. virtual_out = param->value.ui;
  598. break;
  599. }
  600. }
  601. Jack::JackDriverClientInterface* driver = new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine, table);
  602. if (driver->Open(1, 1, virtual_in, virtual_out, false, "in", "out", 0, 0) == 0) {
  603. return driver;
  604. } else {
  605. delete driver;
  606. return NULL;
  607. }
  608. }
  609. #ifdef __cplusplus
  610. }
  611. #endif