MIDI thread RT priority reworked a bit on OSX.
[jack2.git] / macosx / coremidi / JackCoreMidiDriver.cpp
blob85d01fcfbe0d3a1d8b9798ae9fcb0c62cdbcb886
1 /*
2 Copyright (C) 2009 Grame
3 Copyright (C) 2011 Devin Anderson
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <stdexcept>
23 #include <mach/mach_time.h>
25 #include "JackCoreMidiDriver.h"
26 #include "JackCoreMidiUtil.h"
27 #include "JackEngineControl.h"
29 using Jack::JackCoreMidiDriver;
31 ///////////////////////////////////////////////////////////////////////////////
32 // Static callbacks
33 ///////////////////////////////////////////////////////////////////////////////
35 void
36 JackCoreMidiDriver::HandleInputEvent(const MIDIPacketList *packet_list,
37 void *driver, void *port)
39 ((JackCoreMidiPhysicalInputPort *) port)->ProcessCoreMidi(packet_list);
42 void
43 JackCoreMidiDriver::HandleNotificationEvent(const MIDINotification *message,
44 void *driver)
46 ((JackCoreMidiDriver *) driver)->HandleNotification(message);
49 ///////////////////////////////////////////////////////////////////////////////
50 // Class
51 ///////////////////////////////////////////////////////////////////////////////
53 JackCoreMidiDriver::JackCoreMidiDriver(const char *name, const char *alias,
54 JackLockedEngine *engine,
55 JackSynchro *table):
56 JackMidiDriver(name, alias, engine, table)
58 mach_timebase_info_data_t info;
59 kern_return_t result = mach_timebase_info(&info);
60 if (result != KERN_SUCCESS) {
61 throw std::runtime_error(mach_error_string(result));
63 client = 0;
64 fCaptureChannels = 0;
65 fPlaybackChannels = 0;
66 num_physical_inputs = 0;
67 num_physical_outputs = 0;
68 num_virtual_inputs = 0;
69 num_virtual_outputs = 0;
70 physical_input_ports = 0;
71 physical_output_ports = 0;
72 time_ratio = (((double) info.numer) / info.denom) / 1000.0;
73 virtual_input_ports = 0;
74 virtual_output_ports = 0;
77 JackCoreMidiDriver::~JackCoreMidiDriver()
79 Stop();
80 Close();
83 int
84 JackCoreMidiDriver::Attach()
86 jack_nframes_t buffer_size = fEngineControl->fBufferSize;
87 jack_port_id_t index;
88 jack_nframes_t latency = buffer_size;
89 jack_latency_range_t latency_range;
90 const char *name;
91 JackPort *port;
92 JackCoreMidiPort *port_obj;
93 latency_range.max = latency;
94 latency_range.min = latency;
96 // Physical inputs
97 for (int i = 0; i < num_physical_inputs; i++) {
98 port_obj = physical_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 physical "
105 "input port with name '%s'.", name);
106 // X: Do we need to deallocate ports?
107 return -1;
109 port = fGraphManager->GetPort(index);
110 port->SetAlias(port_obj->GetAlias());
111 port->SetLatencyRange(JackCaptureLatency, &latency_range);
112 fCapturePortList[i] = index;
115 // Virtual inputs
116 for (int i = 0; i < num_virtual_inputs; i++) {
117 port_obj = virtual_input_ports[i];
118 name = port_obj->GetName();
119 index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
120 JACK_DEFAULT_MIDI_TYPE,
121 CaptureDriverFlags, buffer_size);
122 if (index == NO_PORT) {
123 jack_error("JackCoreMidiDriver::Attach - cannot register virtual "
124 "input port with name '%s'.", name);
125 // X: Do we need to deallocate ports?
126 return -1;
128 port = fGraphManager->GetPort(index);
129 port->SetAlias(port_obj->GetAlias());
130 port->SetLatencyRange(JackCaptureLatency, &latency_range);
131 fCapturePortList[num_physical_inputs + i] = index;
134 if (! fEngineControl->fSyncMode) {
135 latency += buffer_size;
136 latency_range.max = latency;
137 latency_range.min = latency;
140 // Physical outputs
141 for (int i = 0; i < num_physical_outputs; i++) {
142 port_obj = physical_output_ports[i];
143 name = port_obj->GetName();
144 index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
145 JACK_DEFAULT_MIDI_TYPE,
146 PlaybackDriverFlags, buffer_size);
147 if (index == NO_PORT) {
148 jack_error("JackCoreMidiDriver::Attach - cannot register physical "
149 "output port with name '%s'.", name);
150 // X: Do we need to deallocate ports?
151 return -1;
153 port = fGraphManager->GetPort(index);
154 port->SetAlias(port_obj->GetAlias());
155 port->SetLatencyRange(JackPlaybackLatency, &latency_range);
156 fPlaybackPortList[i] = index;
159 // Virtual outputs
160 for (int i = 0; i < num_virtual_outputs; i++) {
161 port_obj = virtual_output_ports[i];
162 name = port_obj->GetName();
163 index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
164 JACK_DEFAULT_MIDI_TYPE,
165 PlaybackDriverFlags, buffer_size);
166 if (index == NO_PORT) {
167 jack_error("JackCoreMidiDriver::Attach - cannot register virtual "
168 "output port with name '%s'.", name);
169 // X: Do we need to deallocate ports?
170 return -1;
172 port = fGraphManager->GetPort(index);
173 port->SetAlias(port_obj->GetAlias());
174 port->SetLatencyRange(JackPlaybackLatency, &latency_range);
175 fPlaybackPortList[num_physical_outputs + i] = index;
178 return 0;
182 JackCoreMidiDriver::Close()
184 int result = 0;
185 OSStatus status;
186 if (physical_input_ports) {
187 for (int i = 0; i < num_physical_inputs; i++) {
188 delete physical_input_ports[i];
190 delete[] physical_input_ports;
191 num_physical_inputs = 0;
192 physical_input_ports = 0;
193 status = MIDIPortDispose(internal_input);
194 if (status != noErr) {
195 WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose",
196 status);
197 result = -1;
200 if (physical_output_ports) {
201 for (int i = 0; i < num_physical_outputs; i++) {
202 delete physical_output_ports[i];
204 delete[] physical_output_ports;
205 num_physical_outputs = 0;
206 physical_output_ports = 0;
207 status = MIDIPortDispose(internal_output);
208 if (status != noErr) {
209 WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose",
210 status);
211 result = -1;
214 if (virtual_input_ports) {
215 for (int i = 0; i < num_virtual_inputs; i++) {
216 delete virtual_input_ports[i];
218 delete[] virtual_input_ports;
219 num_virtual_inputs = 0;
220 virtual_input_ports = 0;
222 if (virtual_output_ports) {
223 for (int i = 0; i < num_virtual_outputs; i++) {
224 delete virtual_output_ports[i];
226 delete[] virtual_output_ports;
227 num_virtual_outputs = 0;
228 virtual_output_ports = 0;
230 if (client) {
231 status = MIDIClientDispose(client);
232 if (status != noErr) {
233 WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientDispose",
234 status);
235 result = -1;
237 client = 0;
239 return result;
242 void
243 JackCoreMidiDriver::HandleNotification(const MIDINotification *message)
245 // Empty
249 JackCoreMidiDriver::Open(bool capturing, bool playing, int in_channels,
250 int out_channels, bool monitor,
251 const char* capture_driver_name,
252 const char* playback_driver_name,
253 jack_nframes_t capture_latency,
254 jack_nframes_t playback_latency)
256 int pi_count = 0;
257 int po_count = 0;
258 int vi_count = 0;
259 int vo_count = 0;
260 ItemCount potential_po_count;
261 ItemCount potential_pi_count;
263 CFStringRef name = CFStringCreateWithCString(0, "JackMidi",
264 CFStringGetSystemEncoding());
265 if (! name) {
266 jack_error("JackCoreMidiDriver::Open - failed to allocate memory for "
267 "client name string");
268 return -1;
271 OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this,
272 &client);
274 CFRelease(name);
275 if (status != noErr) {
276 WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientCreate",
277 status);
278 return -1;
280 char *client_name = fClientControl.fName;
282 // Allocate and connect virtual inputs
283 if (in_channels) {
284 try {
285 virtual_input_ports =
286 new JackCoreMidiVirtualInputPort*[in_channels];
287 } catch (std::exception e) {
288 jack_error("JackCoreMidiDriver::Open - while creating virtual "
289 "input port array: %s", e.what());
290 goto destroy_client;
292 for (vi_count = 0; vi_count < in_channels; vi_count++) {
293 try {
294 virtual_input_ports[vi_count] =
295 new JackCoreMidiVirtualInputPort(fAliasName, client_name,
296 capture_driver_name,
297 vi_count, client,
298 time_ratio);
299 } catch (std::exception e) {
300 jack_error("JackCoreMidiDriver::Open - while creating virtual "
301 "input port: %s", e.what());
302 goto destroy_virtual_input_ports;
307 // Allocate and connect virtual outputs
308 if (out_channels) {
309 try {
310 virtual_output_ports =
311 new JackCoreMidiVirtualOutputPort*[out_channels];
312 } catch (std::exception e) {
313 jack_error("JackCoreMidiDriver::Open - while creating virtual "
314 "output port array: %s", e.what());
315 goto destroy_virtual_input_ports;
317 for (vo_count = 0; vo_count < out_channels; vo_count++) {
318 try {
319 virtual_output_ports[vo_count] =
320 new JackCoreMidiVirtualOutputPort(fAliasName, client_name,
321 playback_driver_name,
322 vo_count, client,
323 time_ratio);
324 } catch (std::exception e) {
325 jack_error("JackCoreMidiDriver::Open - while creating virtual "
326 "output port: %s", e.what());
327 goto destroy_virtual_output_ports;
332 // Allocate and connect physical inputs
333 potential_pi_count = MIDIGetNumberOfSources();
334 if (potential_pi_count) {
335 status = MIDIInputPortCreate(client, CFSTR("Physical Input Port"),
336 HandleInputEvent, this, &internal_input);
337 if (status != noErr) {
338 WriteMacOSError("JackCoreMidiDriver::Open", "MIDIInputPortCreate",
339 status);
340 goto destroy_virtual_output_ports;
342 try {
343 physical_input_ports =
344 new JackCoreMidiPhysicalInputPort*[potential_pi_count];
345 } catch (std::exception e) {
346 jack_error("JackCoreMidiDriver::Open - while creating physical "
347 "input port array: %s", e.what());
348 goto destroy_internal_input_port;
350 for (ItemCount i = 0; i < potential_pi_count; i++) {
351 try {
352 physical_input_ports[pi_count] =
353 new JackCoreMidiPhysicalInputPort(fAliasName, client_name,
354 capture_driver_name, i,
355 client, internal_input,
356 time_ratio);
357 } catch (std::exception e) {
358 jack_error("JackCoreMidiDriver::Open - while creating "
359 "physical input port: %s", e.what());
360 continue;
362 pi_count++;
366 // Allocate and connect physical outputs
367 potential_po_count = MIDIGetNumberOfDestinations();
368 if (potential_po_count) {
369 status = MIDIOutputPortCreate(client, CFSTR("Physical Output Port"),
370 &internal_output);
371 if (status != noErr) {
372 WriteMacOSError("JackCoreMidiDriver::Open", "MIDIOutputPortCreate",
373 status);
374 goto destroy_physical_input_ports;
376 try {
377 physical_output_ports =
378 new JackCoreMidiPhysicalOutputPort*[potential_po_count];
379 } catch (std::exception e) {
380 jack_error("JackCoreMidiDriver::Open - while creating physical "
381 "output port array: %s", e.what());
382 goto destroy_internal_output_port;
384 for (ItemCount i = 0; i < potential_po_count; i++) {
385 try {
386 physical_output_ports[po_count] =
387 new JackCoreMidiPhysicalOutputPort(fAliasName, client_name,
388 playback_driver_name, i,
389 client, internal_output,
390 time_ratio);
391 } catch (std::exception e) {
392 jack_error("JackCoreMidiDriver::Open - while creating "
393 "physical output port: %s", e.what());
394 continue;
396 po_count++;
400 if (! (pi_count || po_count || in_channels || out_channels)) {
401 jack_error("JackCoreMidiDriver::Open - no CoreMIDI inputs or outputs "
402 "found, and no virtual ports allocated.");
403 } else if (! JackMidiDriver::Open(capturing, playing,
404 in_channels + pi_count,
405 out_channels + po_count, monitor,
406 capture_driver_name,
407 playback_driver_name, capture_latency,
408 playback_latency)) {
409 num_physical_inputs = pi_count;
410 num_physical_outputs = po_count;
411 num_virtual_inputs = in_channels;
412 num_virtual_outputs = out_channels;
413 return 0;
416 // Cleanup
417 if (physical_output_ports) {
418 for (int i = 0; i < po_count; i++) {
419 delete physical_output_ports[i];
421 delete[] physical_output_ports;
422 physical_output_ports = 0;
424 destroy_internal_output_port:
425 status = MIDIPortDispose(internal_output);
426 if (status != noErr) {
427 WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status);
429 destroy_physical_input_ports:
430 if (physical_input_ports) {
431 for (int i = 0; i < pi_count; i++) {
432 delete physical_input_ports[i];
434 delete[] physical_input_ports;
435 physical_input_ports = 0;
437 destroy_internal_input_port:
438 status = MIDIPortDispose(internal_input);
439 if (status != noErr) {
440 WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status);
442 destroy_virtual_output_ports:
443 if (virtual_output_ports) {
444 for (int i = 0; i < vo_count; i++) {
445 delete virtual_output_ports[i];
447 delete[] virtual_output_ports;
448 virtual_output_ports = 0;
450 destroy_virtual_input_ports:
451 if (virtual_input_ports) {
452 for (int i = 0; i < vi_count; i++) {
453 delete virtual_input_ports[i];
455 delete[] virtual_input_ports;
456 virtual_input_ports = 0;
458 destroy_client:
459 status = MIDIClientDispose(client);
460 if (status != noErr) {
461 WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientDispose",
462 status);
464 client = 0;
465 return -1;
469 JackCoreMidiDriver::Read()
471 jack_nframes_t buffer_size = fEngineControl->fBufferSize;
472 for (int i = 0; i < num_physical_inputs; i++) {
473 physical_input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size);
475 for (int i = 0; i < num_virtual_inputs; i++) {
476 virtual_input_ports[i]->
477 ProcessJack(GetInputBuffer(num_physical_inputs + i), buffer_size);
479 return 0;
483 JackCoreMidiDriver::Start()
485 jack_info("JackCoreMidiDriver::Start - Starting driver.");
487 JackMidiDriver::Start();
489 int pi_count = 0;
490 int po_count = 0;
491 int vi_count = 0;
492 int vo_count = 0;
494 jack_info("JackCoreMidiDriver::Start - Enabling physical input ports.");
496 for (; pi_count < num_physical_inputs; pi_count++) {
497 if (physical_input_ports[pi_count]->Start() < 0) {
498 jack_error("JackCoreMidiDriver::Start - Failed to enable physical "
499 "input port.");
500 goto stop_physical_input_ports;
504 jack_info("JackCoreMidiDriver::Start - Enabling physical output ports.");
506 for (; po_count < num_physical_outputs; po_count++) {
507 if (physical_output_ports[po_count]->Start() < 0) {
508 jack_error("JackCoreMidiDriver::Start - Failed to enable physical "
509 "output port.");
510 goto stop_physical_output_ports;
514 jack_info("JackCoreMidiDriver::Start - Enabling virtual input ports.");
516 for (; vi_count < num_virtual_inputs; vi_count++) {
517 if (virtual_input_ports[vi_count]->Start() < 0) {
518 jack_error("JackCoreMidiDriver::Start - Failed to enable virtual "
519 "input port.");
520 goto stop_virtual_input_ports;
524 jack_info("JackCoreMidiDriver::Start - Enabling virtual output ports.");
526 for (; vo_count < num_virtual_outputs; vo_count++) {
527 if (virtual_output_ports[vo_count]->Start() < 0) {
528 jack_error("JackCoreMidiDriver::Start - Failed to enable virtual "
529 "output port.");
530 goto stop_virtual_output_ports;
534 jack_info("JackCoreMidiDriver::Start - Driver started.");
536 return 0;
538 stop_virtual_output_ports:
539 for (int i = 0; i < vo_count; i++) {
540 if (virtual_output_ports[i]->Stop() < 0) {
541 jack_error("JackCoreMidiDriver::Start - Failed to disable virtual "
542 "output port.");
545 stop_virtual_input_ports:
546 for (int i = 0; i < vi_count; i++) {
547 if (virtual_input_ports[i]->Stop() < 0) {
548 jack_error("JackCoreMidiDriver::Start - Failed to disable virtual "
549 "input port.");
552 stop_physical_output_ports:
553 for (int i = 0; i < po_count; i++) {
554 if (physical_output_ports[i]->Stop() < 0) {
555 jack_error("JackCoreMidiDriver::Start - Failed to disable "
556 "physical output port.");
559 stop_physical_input_ports:
560 for (int i = 0; i < pi_count; i++) {
561 if (physical_input_ports[i]->Stop() < 0) {
562 jack_error("JackCoreMidiDriver::Start - Failed to disable "
563 "physical input port.");
567 return -1;
571 JackCoreMidiDriver::Stop()
573 int result = 0;
575 jack_info("JackCoreMidiDriver::Stop - disabling physical input ports.");
577 for (int i = 0; i < num_physical_inputs; i++) {
578 if (physical_input_ports[i]->Stop() < 0) {
579 jack_error("JackCoreMidiDriver::Stop - Failed to disable physical "
580 "input port.");
581 result = -1;
585 jack_info("JackCoreMidiDriver::Stop - disabling physical output ports.");
587 for (int i = 0; i < num_physical_outputs; i++) {
588 if (physical_output_ports[i]->Stop() < 0) {
589 jack_error("JackCoreMidiDriver::Stop - Failed to disable physical "
590 "output port.");
591 result = -1;
595 jack_info("JackCoreMidiDriver::Stop - disabling virtual input ports.");
597 for (int i = 0; i < num_virtual_inputs; i++) {
598 if (virtual_input_ports[i]->Stop() < 0) {
599 jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual "
600 "input port.");
601 result = -1;
605 jack_info("JackCoreMidiDriver::Stop - disabling virtual output ports.");
607 for (int i = 0; i < num_virtual_outputs; i++) {
608 if (virtual_output_ports[i]->Stop() < 0) {
609 jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual "
610 "output port.");
611 result = -1;
615 return result;
619 JackCoreMidiDriver::Write()
621 jack_nframes_t buffer_size = fEngineControl->fBufferSize;
622 for (int i = 0; i < num_physical_outputs; i++) {
623 physical_output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size);
625 for (int i = 0; i < num_virtual_outputs; i++) {
626 virtual_output_ports[i]->
627 ProcessJack(GetOutputBuffer(num_physical_outputs + i),
628 buffer_size);
630 return 0;
633 #ifdef __cplusplus
634 extern "C" {
635 #endif
637 SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor()
639 jack_driver_desc_t * desc;
640 unsigned int i;
642 desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t));
643 strcpy(desc->name, "coremidi"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1
644 strcpy(desc->desc, "Apple CoreMIDI API based MIDI backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1
646 desc->nparams = 2;
647 desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t));
649 i = 0;
650 strcpy(desc->params[i].name, "inchannels");
651 desc->params[i].character = 'i';
652 desc->params[i].type = JackDriverParamInt;
653 desc->params[i].value.ui = 0;
654 strcpy(desc->params[i].short_desc, "CoreMIDI virtual bus");
655 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
657 i++;
658 strcpy(desc->params[i].name, "outchannels");
659 desc->params[i].character = 'o';
660 desc->params[i].type = JackDriverParamInt;
661 desc->params[i].value.ui = 0;
662 strcpy(desc->params[i].short_desc, "CoreMIDI virtual bus");
663 strcpy(desc->params[i].long_desc, desc->params[i].short_desc);
665 return desc;
668 SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
670 const JSList * node;
671 const jack_driver_param_t * param;
672 int virtual_in = 0;
673 int virtual_out = 0;
675 for (node = params; node; node = jack_slist_next (node)) {
676 param = (const jack_driver_param_t *) node->data;
678 switch (param->character) {
680 case 'i':
681 virtual_in = param->value.ui;
682 break;
684 case 'o':
685 virtual_out = param->value.ui;
686 break;
690 Jack::JackDriverClientInterface* driver = new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine, table);
691 if (driver->Open(1, 1, virtual_in, virtual_out, false, "in", "out", 0, 0) == 0) {
692 return driver;
693 } else {
694 delete driver;
695 return NULL;
699 #ifdef __cplusplus
701 #endif