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.

690 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. fEngine->NotifyPortRegistration(index, true);
  95. }
  96. // Virtual inputs
  97. for (int i = 0; i < num_virtual_inputs; i++) {
  98. port_obj = virtual_input_ports[i];
  99. name = port_obj->GetName();
  100. index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
  101. JACK_DEFAULT_MIDI_TYPE,
  102. CaptureDriverFlags, buffer_size);
  103. if (index == NO_PORT) {
  104. jack_error("JackCoreMidiDriver::Attach - cannot register virtual "
  105. "input port with name '%s'.", name);
  106. // X: Do we need to deallocate ports?
  107. return -1;
  108. }
  109. port = fGraphManager->GetPort(index);
  110. port->SetAlias(port_obj->GetAlias());
  111. port->SetLatencyRange(JackCaptureLatency, &latency_range);
  112. fCapturePortList[num_physical_inputs + i] = index;
  113. fEngine->NotifyPortRegistration(index, true);
  114. }
  115. if (! fEngineControl->fSyncMode) {
  116. latency += buffer_size;
  117. latency_range.max = latency;
  118. latency_range.min = latency;
  119. }
  120. // Physical outputs
  121. for (int i = 0; i < num_physical_outputs; i++) {
  122. port_obj = physical_output_ports[i];
  123. name = port_obj->GetName();
  124. index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
  125. JACK_DEFAULT_MIDI_TYPE,
  126. PlaybackDriverFlags, buffer_size);
  127. if (index == NO_PORT) {
  128. jack_error("JackCoreMidiDriver::Attach - cannot register physical "
  129. "output port with name '%s'.", name);
  130. // X: Do we need to deallocate ports?
  131. return -1;
  132. }
  133. port = fGraphManager->GetPort(index);
  134. port->SetAlias(port_obj->GetAlias());
  135. port->SetLatencyRange(JackPlaybackLatency, &latency_range);
  136. fPlaybackPortList[i] = index;
  137. fEngine->NotifyPortRegistration(index, true);
  138. }
  139. // Virtual outputs
  140. for (int i = 0; i < num_virtual_outputs; i++) {
  141. port_obj = virtual_output_ports[i];
  142. name = port_obj->GetName();
  143. index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
  144. JACK_DEFAULT_MIDI_TYPE,
  145. PlaybackDriverFlags, buffer_size);
  146. if (index == NO_PORT) {
  147. jack_error("JackCoreMidiDriver::Attach - cannot register virtual "
  148. "output port with name '%s'.", name);
  149. // X: Do we need to deallocate ports?
  150. return -1;
  151. }
  152. port = fGraphManager->GetPort(index);
  153. port->SetAlias(port_obj->GetAlias());
  154. port->SetLatencyRange(JackPlaybackLatency, &latency_range);
  155. fPlaybackPortList[num_physical_outputs + i] = index;
  156. fEngine->NotifyPortRegistration(index, true);
  157. }
  158. return 0;
  159. }
  160. int
  161. JackCoreMidiDriver::Close()
  162. {
  163. // Generic MIDI driver close
  164. int result = JackMidiDriver::Close();
  165. OSStatus status;
  166. if (physical_input_ports) {
  167. for (int i = 0; i < num_physical_inputs; i++) {
  168. delete physical_input_ports[i];
  169. }
  170. delete[] physical_input_ports;
  171. num_physical_inputs = 0;
  172. physical_input_ports = 0;
  173. status = MIDIPortDispose(internal_input);
  174. if (status != noErr) {
  175. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose",
  176. status);
  177. result = -1;
  178. }
  179. }
  180. if (physical_output_ports) {
  181. for (int i = 0; i < num_physical_outputs; i++) {
  182. delete physical_output_ports[i];
  183. }
  184. delete[] physical_output_ports;
  185. num_physical_outputs = 0;
  186. physical_output_ports = 0;
  187. status = MIDIPortDispose(internal_output);
  188. if (status != noErr) {
  189. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose",
  190. status);
  191. result = -1;
  192. }
  193. }
  194. if (virtual_input_ports) {
  195. for (int i = 0; i < num_virtual_inputs; i++) {
  196. delete virtual_input_ports[i];
  197. }
  198. delete[] virtual_input_ports;
  199. num_virtual_inputs = 0;
  200. virtual_input_ports = 0;
  201. }
  202. if (virtual_output_ports) {
  203. for (int i = 0; i < num_virtual_outputs; i++) {
  204. delete virtual_output_ports[i];
  205. }
  206. delete[] virtual_output_ports;
  207. num_virtual_outputs = 0;
  208. virtual_output_ports = 0;
  209. }
  210. if (client) {
  211. status = MIDIClientDispose(client);
  212. if (status != noErr) {
  213. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientDispose",
  214. status);
  215. result = -1;
  216. }
  217. client = 0;
  218. }
  219. return result;
  220. }
  221. void
  222. JackCoreMidiDriver::HandleNotification(const MIDINotification *message)
  223. {
  224. // Empty
  225. }
  226. int
  227. JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels,
  228. int out_channels, bool monitor,
  229. const char* capture_driver_name,
  230. const char* playback_driver_name,
  231. jack_nframes_t capture_latency,
  232. jack_nframes_t playback_latency)
  233. {
  234. int pi_count = 0;
  235. int po_count = 0;
  236. int vi_count = 0;
  237. int vo_count = 0;
  238. ItemCount potential_po_count;
  239. ItemCount potential_pi_count;
  240. CFStringRef name = CFStringCreateWithCString(0, "JackMidi",
  241. CFStringGetSystemEncoding());
  242. if (! name) {
  243. jack_error("JackCoreMidiDriver::Open - failed to allocate memory for "
  244. "client name string");
  245. return -1;
  246. }
  247. OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this,
  248. &client);
  249. CFRelease(name);
  250. if (status != noErr) {
  251. WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientCreate",
  252. status);
  253. return -1;
  254. }
  255. char *client_name = fClientControl.fName;
  256. // Allocate and connect physical inputs
  257. potential_pi_count = MIDIGetNumberOfSources();
  258. if (potential_pi_count) {
  259. status = MIDIInputPortCreate(client, CFSTR("Physical Input Port"),
  260. HandleInputEvent, this, &internal_input);
  261. if (status != noErr) {
  262. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIInputPortCreate",
  263. status);
  264. goto destroy_virtual_output_ports;
  265. }
  266. try {
  267. physical_input_ports =
  268. new JackCoreMidiPhysicalInputPort*[potential_pi_count];
  269. } catch (std::exception e) {
  270. jack_error("JackCoreMidiDriver::Open - while creating physical "
  271. "input port array: %s", e.what());
  272. goto destroy_internal_input_port;
  273. }
  274. for (ItemCount i = 0; i < potential_pi_count; i++) {
  275. try {
  276. physical_input_ports[pi_count] =
  277. new JackCoreMidiPhysicalInputPort(fAliasName, client_name,
  278. capture_driver_name, i,
  279. client, internal_input,
  280. time_ratio);
  281. } catch (std::exception e) {
  282. jack_error("JackCoreMidiDriver::Open - while creating "
  283. "physical input port: %s", e.what());
  284. goto destroy_internal_input_port;
  285. }
  286. pi_count++;
  287. }
  288. }
  289. // Allocate and connect physical outputs
  290. potential_po_count = MIDIGetNumberOfDestinations();
  291. if (potential_po_count) {
  292. status = MIDIOutputPortCreate(client, CFSTR("Physical Output Port"),
  293. &internal_output);
  294. if (status != noErr) {
  295. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIOutputPortCreate",
  296. status);
  297. goto destroy_physical_input_ports;
  298. }
  299. try {
  300. physical_output_ports =
  301. new JackCoreMidiPhysicalOutputPort*[potential_po_count];
  302. } catch (std::exception e) {
  303. jack_error("JackCoreMidiDriver::Open - while creating physical "
  304. "output port array: %s", e.what());
  305. goto destroy_internal_output_port;
  306. }
  307. for (ItemCount i = 0; i < potential_po_count; i++) {
  308. try {
  309. physical_output_ports[po_count] =
  310. new JackCoreMidiPhysicalOutputPort(fAliasName, client_name,
  311. playback_driver_name, i,
  312. client, internal_output,
  313. time_ratio);
  314. } catch (std::exception e) {
  315. jack_error("JackCoreMidiDriver::Open - while creating "
  316. "physical output port: %s", e.what());
  317. goto destroy_internal_output_port;
  318. }
  319. po_count++;
  320. }
  321. }
  322. // Allocate and connect virtual inputs
  323. if (in_channels) {
  324. try {
  325. virtual_input_ports =
  326. new JackCoreMidiVirtualInputPort*[in_channels];
  327. } catch (std::exception e) {
  328. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  329. "input port array: %s", e.what());
  330. goto destroy_client;
  331. }
  332. for (vi_count = 0; vi_count < in_channels; vi_count++) {
  333. try {
  334. virtual_input_ports[vi_count] =
  335. new JackCoreMidiVirtualInputPort(fAliasName, client_name,
  336. capture_driver_name,
  337. vi_count + pi_count, client,
  338. time_ratio);
  339. } catch (std::exception e) {
  340. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  341. "input port: %s", e.what());
  342. goto destroy_virtual_input_ports;
  343. }
  344. }
  345. }
  346. // Allocate and connect virtual outputs
  347. if (out_channels) {
  348. try {
  349. virtual_output_ports =
  350. new JackCoreMidiVirtualOutputPort*[out_channels];
  351. } catch (std::exception e) {
  352. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  353. "output port array: %s", e.what());
  354. goto destroy_virtual_input_ports;
  355. }
  356. for (vo_count = 0; vo_count < out_channels; vo_count++) {
  357. try {
  358. virtual_output_ports[vo_count] =
  359. new JackCoreMidiVirtualOutputPort(fAliasName, client_name,
  360. playback_driver_name,
  361. vo_count + po_count, client,
  362. time_ratio);
  363. } catch (std::exception e) {
  364. jack_error("JackCoreMidiDriver::Open - while creating virtual "
  365. "output port: %s", e.what());
  366. goto destroy_virtual_output_ports;
  367. }
  368. }
  369. }
  370. if (! (pi_count || po_count || in_channels || out_channels)) {
  371. jack_error("JackCoreMidiDriver::Open - no CoreMIDI inputs or outputs "
  372. "found, and no virtual ports allocated.");
  373. } else if (! JackMidiDriver::Open(capturing, playing,
  374. in_channels + pi_count,
  375. out_channels + po_count, monitor,
  376. capture_driver_name,
  377. playback_driver_name, capture_latency,
  378. playback_latency)) {
  379. num_physical_inputs = pi_count;
  380. num_physical_outputs = po_count;
  381. num_virtual_inputs = in_channels;
  382. num_virtual_outputs = out_channels;
  383. return 0;
  384. }
  385. // Cleanup
  386. if (physical_output_ports) {
  387. for (int i = 0; i < po_count; i++) {
  388. delete physical_output_ports[i];
  389. }
  390. delete[] physical_output_ports;
  391. physical_output_ports = 0;
  392. }
  393. destroy_internal_output_port:
  394. status = MIDIPortDispose(internal_output);
  395. if (status != noErr) {
  396. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status);
  397. }
  398. destroy_physical_input_ports:
  399. if (physical_input_ports) {
  400. for (int i = 0; i < pi_count; i++) {
  401. delete physical_input_ports[i];
  402. }
  403. delete[] physical_input_ports;
  404. physical_input_ports = 0;
  405. }
  406. destroy_internal_input_port:
  407. status = MIDIPortDispose(internal_input);
  408. if (status != noErr) {
  409. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status);
  410. }
  411. destroy_virtual_output_ports:
  412. if (virtual_output_ports) {
  413. for (int i = 0; i < vo_count; i++) {
  414. delete virtual_output_ports[i];
  415. }
  416. delete[] virtual_output_ports;
  417. virtual_output_ports = 0;
  418. }
  419. destroy_virtual_input_ports:
  420. if (virtual_input_ports) {
  421. for (int i = 0; i < vi_count; i++) {
  422. delete virtual_input_ports[i];
  423. }
  424. delete[] virtual_input_ports;
  425. virtual_input_ports = 0;
  426. }
  427. destroy_client:
  428. status = MIDIClientDispose(client);
  429. if (status != noErr) {
  430. WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientDispose",
  431. status);
  432. }
  433. client = 0;
  434. return -1;
  435. }
  436. int
  437. JackCoreMidiDriver::Read()
  438. {
  439. jack_nframes_t buffer_size = fEngineControl->fBufferSize;
  440. for (int i = 0; i < num_physical_inputs; i++) {
  441. physical_input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size);
  442. }
  443. for (int i = 0; i < num_virtual_inputs; i++) {
  444. virtual_input_ports[i]->
  445. ProcessJack(GetInputBuffer(num_physical_inputs + i), buffer_size);
  446. }
  447. return 0;
  448. }
  449. int
  450. JackCoreMidiDriver::Start()
  451. {
  452. jack_info("JackCoreMidiDriver::Start - Starting driver.");
  453. JackMidiDriver::Start();
  454. int pi_count = 0;
  455. int po_count = 0;
  456. int vi_count = 0;
  457. int vo_count = 0;
  458. jack_info("JackCoreMidiDriver::Start - Enabling physical input ports.");
  459. for (; pi_count < num_physical_inputs; pi_count++) {
  460. if (physical_input_ports[pi_count]->Start() < 0) {
  461. jack_error("JackCoreMidiDriver::Start - Failed to enable physical "
  462. "input port.");
  463. goto stop_physical_input_ports;
  464. }
  465. }
  466. jack_info("JackCoreMidiDriver::Start - Enabling physical output ports.");
  467. for (; po_count < num_physical_outputs; po_count++) {
  468. if (physical_output_ports[po_count]->Start() < 0) {
  469. jack_error("JackCoreMidiDriver::Start - Failed to enable physical "
  470. "output port.");
  471. goto stop_physical_output_ports;
  472. }
  473. }
  474. jack_info("JackCoreMidiDriver::Start - Enabling virtual input ports.");
  475. for (; vi_count < num_virtual_inputs; vi_count++) {
  476. if (virtual_input_ports[vi_count]->Start() < 0) {
  477. jack_error("JackCoreMidiDriver::Start - Failed to enable virtual "
  478. "input port.");
  479. goto stop_virtual_input_ports;
  480. }
  481. }
  482. jack_info("JackCoreMidiDriver::Start - Enabling virtual output ports.");
  483. for (; vo_count < num_virtual_outputs; vo_count++) {
  484. if (virtual_output_ports[vo_count]->Start() < 0) {
  485. jack_error("JackCoreMidiDriver::Start - Failed to enable virtual "
  486. "output port.");
  487. goto stop_virtual_output_ports;
  488. }
  489. }
  490. jack_info("JackCoreMidiDriver::Start - Driver started.");
  491. return 0;
  492. stop_virtual_output_ports:
  493. for (int i = 0; i < vo_count; i++) {
  494. if (virtual_output_ports[i]->Stop() < 0) {
  495. jack_error("JackCoreMidiDriver::Start - Failed to disable virtual "
  496. "output port.");
  497. }
  498. }
  499. stop_virtual_input_ports:
  500. for (int i = 0; i < vi_count; i++) {
  501. if (virtual_input_ports[i]->Stop() < 0) {
  502. jack_error("JackCoreMidiDriver::Start - Failed to disable virtual "
  503. "input port.");
  504. }
  505. }
  506. stop_physical_output_ports:
  507. for (int i = 0; i < po_count; i++) {
  508. if (physical_output_ports[i]->Stop() < 0) {
  509. jack_error("JackCoreMidiDriver::Start - Failed to disable "
  510. "physical output port.");
  511. }
  512. }
  513. stop_physical_input_ports:
  514. for (int i = 0; i < pi_count; i++) {
  515. if (physical_input_ports[i]->Stop() < 0) {
  516. jack_error("JackCoreMidiDriver::Start - Failed to disable "
  517. "physical input port.");
  518. }
  519. }
  520. return -1;
  521. }
  522. int
  523. JackCoreMidiDriver::Stop()
  524. {
  525. int result = 0;
  526. jack_info("JackCoreMidiDriver::Stop - disabling physical input ports.");
  527. for (int i = 0; i < num_physical_inputs; i++) {
  528. if (physical_input_ports[i]->Stop() < 0) {
  529. jack_error("JackCoreMidiDriver::Stop - Failed to disable physical "
  530. "input port.");
  531. result = -1;
  532. }
  533. }
  534. jack_info("JackCoreMidiDriver::Stop - disabling physical output ports.");
  535. for (int i = 0; i < num_physical_outputs; i++) {
  536. if (physical_output_ports[i]->Stop() < 0) {
  537. jack_error("JackCoreMidiDriver::Stop - Failed to disable physical "
  538. "output port.");
  539. result = -1;
  540. }
  541. }
  542. jack_info("JackCoreMidiDriver::Stop - disabling virtual input ports.");
  543. for (int i = 0; i < num_virtual_inputs; i++) {
  544. if (virtual_input_ports[i]->Stop() < 0) {
  545. jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual "
  546. "input port.");
  547. result = -1;
  548. }
  549. }
  550. jack_info("JackCoreMidiDriver::Stop - disabling virtual output ports.");
  551. for (int i = 0; i < num_virtual_outputs; i++) {
  552. if (virtual_output_ports[i]->Stop() < 0) {
  553. jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual "
  554. "output port.");
  555. result = -1;
  556. }
  557. }
  558. return result;
  559. }
  560. int
  561. JackCoreMidiDriver::Write()
  562. {
  563. jack_nframes_t buffer_size = fEngineControl->fBufferSize;
  564. for (int i = 0; i < num_physical_outputs; i++) {
  565. physical_output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size);
  566. }
  567. for (int i = 0; i < num_virtual_outputs; i++) {
  568. virtual_output_ports[i]->
  569. ProcessJack(GetOutputBuffer(num_physical_outputs + i),
  570. buffer_size);
  571. }
  572. return 0;
  573. }
  574. #ifdef __cplusplus
  575. extern "C" {
  576. #endif
  577. SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor()
  578. {
  579. jack_driver_desc_t * desc;
  580. jack_driver_desc_filler_t filler;
  581. jack_driver_param_value_t value;
  582. desc = jack_driver_descriptor_construct("coremidi", "Apple CoreMIDI API based MIDI backend", &filler);
  583. value.ui = 0;
  584. jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL);
  585. jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL);
  586. return desc;
  587. }
  588. SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
  589. {
  590. const JSList * node;
  591. const jack_driver_param_t * param;
  592. int virtual_in = 0;
  593. int virtual_out = 0;
  594. for (node = params; node; node = jack_slist_next (node)) {
  595. param = (const jack_driver_param_t *) node->data;
  596. switch (param->character) {
  597. case 'i':
  598. virtual_in = param->value.ui;
  599. break;
  600. case 'o':
  601. virtual_out = param->value.ui;
  602. break;
  603. }
  604. }
  605. Jack::JackDriverClientInterface* driver = new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine, table);
  606. if (driver->Open(1, 1, virtual_in, virtual_out, false, "in", "out", 0, 0) == 0) {
  607. return driver;
  608. } else {
  609. delete driver;
  610. return NULL;
  611. }
  612. }
  613. #ifdef __cplusplus
  614. }
  615. #endif