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.
23 #include <mach/mach_time.h>
25 #include "JackCoreMidiDriver.h"
26 #include "JackCoreMidiUtil.h"
27 #include "JackEngineControl.h"
29 using Jack::JackCoreMidiDriver
;
31 ///////////////////////////////////////////////////////////////////////////////
33 ///////////////////////////////////////////////////////////////////////////////
36 JackCoreMidiDriver::HandleInputEvent(const MIDIPacketList
*packet_list
,
37 void *driver
, void *port
)
39 ((JackCoreMidiPhysicalInputPort
*) port
)->ProcessCoreMidi(packet_list
);
43 JackCoreMidiDriver::HandleNotificationEvent(const MIDINotification
*message
,
46 ((JackCoreMidiDriver
*) driver
)->HandleNotification(message
);
49 ///////////////////////////////////////////////////////////////////////////////
51 ///////////////////////////////////////////////////////////////////////////////
53 JackCoreMidiDriver::JackCoreMidiDriver(const char *name
, const char *alias
,
54 JackLockedEngine
*engine
,
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
));
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()
84 JackCoreMidiDriver::Attach()
86 jack_nframes_t buffer_size
= fEngineControl
->fBufferSize
;
88 jack_nframes_t latency
= buffer_size
;
89 jack_latency_range_t latency_range
;
92 JackCoreMidiPort
*port_obj
;
93 latency_range
.max
= latency
;
94 latency_range
.min
= latency
;
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?
109 port
= fGraphManager
->GetPort(index
);
110 port
->SetAlias(port_obj
->GetAlias());
111 port
->SetLatencyRange(JackCaptureLatency
, &latency_range
);
112 fCapturePortList
[i
] = index
;
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?
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
;
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?
153 port
= fGraphManager
->GetPort(index
);
154 port
->SetAlias(port_obj
->GetAlias());
155 port
->SetLatencyRange(JackPlaybackLatency
, &latency_range
);
156 fPlaybackPortList
[i
] = index
;
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?
172 port
= fGraphManager
->GetPort(index
);
173 port
->SetAlias(port_obj
->GetAlias());
174 port
->SetLatencyRange(JackPlaybackLatency
, &latency_range
);
175 fPlaybackPortList
[num_physical_outputs
+ i
] = index
;
182 JackCoreMidiDriver::Close()
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",
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",
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;
231 status
= MIDIClientDispose(client
);
232 if (status
!= noErr
) {
233 WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientDispose",
243 JackCoreMidiDriver::HandleNotification(const MIDINotification
*message
)
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
)
260 ItemCount potential_po_count
;
261 ItemCount potential_pi_count
;
263 CFStringRef name
= CFStringCreateWithCString(0, "JackMidi",
264 CFStringGetSystemEncoding());
266 jack_error("JackCoreMidiDriver::Open - failed to allocate memory for "
267 "client name string");
271 OSStatus status
= MIDIClientCreate(name
, HandleNotificationEvent
, this,
275 if (status
!= noErr
) {
276 WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientCreate",
280 char *client_name
= fClientControl
.fName
;
282 // Allocate and connect virtual inputs
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());
292 for (vi_count
= 0; vi_count
< in_channels
; vi_count
++) {
294 virtual_input_ports
[vi_count
] =
295 new JackCoreMidiVirtualInputPort(fAliasName
, client_name
,
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
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
++) {
319 virtual_output_ports
[vo_count
] =
320 new JackCoreMidiVirtualOutputPort(fAliasName
, client_name
,
321 playback_driver_name
,
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",
340 goto destroy_virtual_output_ports
;
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
++) {
352 physical_input_ports
[pi_count
] =
353 new JackCoreMidiPhysicalInputPort(fAliasName
, client_name
,
354 capture_driver_name
, i
,
355 client
, internal_input
,
357 } catch (std::exception e
) {
358 jack_error("JackCoreMidiDriver::Open - while creating "
359 "physical input port: %s", e
.what());
366 // Allocate and connect physical outputs
367 potential_po_count
= MIDIGetNumberOfDestinations();
368 if (potential_po_count
) {
369 status
= MIDIOutputPortCreate(client
, CFSTR("Physical Output Port"),
371 if (status
!= noErr
) {
372 WriteMacOSError("JackCoreMidiDriver::Open", "MIDIOutputPortCreate",
374 goto destroy_physical_input_ports
;
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
++) {
386 physical_output_ports
[po_count
] =
387 new JackCoreMidiPhysicalOutputPort(fAliasName
, client_name
,
388 playback_driver_name
, i
,
389 client
, internal_output
,
391 } catch (std::exception e
) {
392 jack_error("JackCoreMidiDriver::Open - while creating "
393 "physical output port: %s", e
.what());
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
,
407 playback_driver_name
, capture_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
;
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;
459 status
= MIDIClientDispose(client
);
460 if (status
!= noErr
) {
461 WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientDispose",
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
);
483 JackCoreMidiDriver::Start()
485 jack_info("JackCoreMidiDriver::Start - Starting driver.");
487 JackMidiDriver::Start();
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 "
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 "
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 "
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 "
530 goto stop_virtual_output_ports
;
534 jack_info("JackCoreMidiDriver::Start - Driver started.");
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 "
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 "
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.");
571 JackCoreMidiDriver::Stop()
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 "
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 "
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 "
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 "
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
),
637 SERVER_EXPORT jack_driver_desc_t
* driver_get_descriptor()
639 jack_driver_desc_t
* desc
;
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
647 desc
->params
= (jack_driver_param_desc_t
*)calloc (desc
->nparams
, sizeof (jack_driver_param_desc_t
));
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
);
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
);
668 SERVER_EXPORT
Jack::JackDriverClientInterface
* driver_initialize(Jack::JackLockedEngine
* engine
, Jack::JackSynchro
* table
, const JSList
* params
)
671 const jack_driver_param_t
* param
;
675 for (node
= params
; node
; node
= jack_slist_next (node
)) {
676 param
= (const jack_driver_param_t
*) node
->data
;
678 switch (param
->character
) {
681 virtual_in
= param
->value
.ui
;
685 virtual_out
= param
->value
.ui
;
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) {