2 Copyright (C) 2009 Grame
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.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "JackCoreMidiDriver.h"
21 #include "JackGraphManager.h"
22 #include "JackServer.h"
23 #include "JackEngineControl.h"
24 #include "JackDriverLoader.h"
26 #include <mach/mach_time.h>
35 static MIDITimeStamp
MIDIGetCurrentHostTime()
37 return mach_absolute_time();
40 void JackCoreMidiDriver::ReadProcAux(const MIDIPacketList
*pktlist
, jack_ringbuffer_t
* ringbuffer
)
42 // Write the number of packets
43 size_t size
= jack_ringbuffer_write(ringbuffer
, (char*)&pktlist
->numPackets
, sizeof(UInt32
));
44 if (size
!= sizeof(UInt32
)) {
45 jack_error("ReadProc : ring buffer is full, skip events...");
49 for (unsigned int i
= 0; i
< pktlist
->numPackets
; ++i
) {
51 MIDIPacket
*packet
= (MIDIPacket
*)pktlist
->packet
;
53 // TODO : use timestamp
55 // Write length of each packet first
56 size
= jack_ringbuffer_write(ringbuffer
, (char*)&packet
->length
, sizeof(UInt16
));
57 if (size
!= sizeof(UInt16
)) {
58 jack_error("ReadProc : ring buffer is full, skip events...");
61 // Write event actual data
62 size
= jack_ringbuffer_write(ringbuffer
, (char*)packet
->data
, packet
->length
);
63 if (size
!= packet
->length
) {
64 jack_error("ReadProc : ring buffer is full, skip events...");
68 packet
= MIDIPacketNext(packet
);
72 void JackCoreMidiDriver::ReadProc(const MIDIPacketList
*pktlist
, void *refCon
, void *connRefCon
)
74 jack_ringbuffer_t
* ringbuffer
= (jack_ringbuffer_t
*)connRefCon
;
75 ReadProcAux(pktlist
, ringbuffer
);
78 void JackCoreMidiDriver::ReadVirtualProc(const MIDIPacketList
*pktlist
, void *refCon
, void *connRefCon
)
80 jack_ringbuffer_t
* ringbuffer
= (jack_ringbuffer_t
*)refCon
;
81 ReadProcAux(pktlist
, ringbuffer
);
84 void JackCoreMidiDriver::NotifyProc(const MIDINotification
*message
, void *refCon
)
86 jack_info("NotifyProc %d", message
->messageID
);
89 JackCoreMidiDriver::JackCoreMidiDriver(const char* name
, const char* alias
, JackLockedEngine
* engine
, JackSynchro
* table
)
90 : JackMidiDriver(name
, alias
, engine
, table
), fMidiClient(NULL
), fInputPort(NULL
), fOutputPort(NULL
), fRealCaptureChannels(0), fRealPlaybackChannels(0)
93 JackCoreMidiDriver::~JackCoreMidiDriver()
96 int JackCoreMidiDriver::Open(bool capturing
,
101 const char* capture_driver_name
,
102 const char* playback_driver_name
,
103 jack_nframes_t capture_latency
,
104 jack_nframes_t playback_latency
)
107 CFStringRef coutputStr
;
110 // Get real input/output number
111 fRealCaptureChannels
= MIDIGetNumberOfSources();
112 fRealPlaybackChannels
= MIDIGetNumberOfDestinations();
114 // Generic JackMidiDriver Open
115 if (JackMidiDriver::Open(capturing
, playing
, inchannels
+ fRealCaptureChannels
, outchannels
+ fRealPlaybackChannels
, monitor
, capture_driver_name
, playback_driver_name
, capture_latency
, playback_latency
) != 0)
118 coutputStr
= CFStringCreateWithCString(0, "JackMidi", CFStringGetSystemEncoding());
119 err
= MIDIClientCreate(coutputStr
, NotifyProc
, this, &fMidiClient
);
120 CFRelease(coutputStr
);
122 jack_error("Cannot create CoreMidi client");
126 err
= MIDIInputPortCreate(fMidiClient
, CFSTR("Input port"), ReadProc
, this, &fInputPort
);
128 jack_error("Cannot open CoreMidi in port\n");
132 err
= MIDIOutputPortCreate(fMidiClient
, CFSTR("Output port"), &fOutputPort
);
134 jack_error("Cannot open CoreMidi out port\n");
138 fMidiDestination
= new MIDIEndpointRef
[inchannels
+ fRealCaptureChannels
];
139 assert(fMidiDestination
);
142 for (int i
= 0; i
< inchannels
; i
++) {
143 std::stringstream num
;
145 str
= "JackMidi" + num
.str();
146 coutputStr
= CFStringCreateWithCString(0, str
.c_str(), CFStringGetSystemEncoding());
147 err
= MIDIDestinationCreate(fMidiClient
, coutputStr
, ReadVirtualProc
, fRingBuffer
[i
], &fMidiDestination
[i
]);
148 CFRelease(coutputStr
);
149 if (!fMidiDestination
[i
]) {
150 jack_error("Cannot create CoreMidi destination");
156 for (int i
= 0; i
< fRealCaptureChannels
; i
++) {
157 fMidiDestination
[i
+ inchannels
] = MIDIGetSource(i
);
158 MIDIPortConnectSource(fInputPort
, fMidiDestination
[i
+ inchannels
], fRingBuffer
[i
+ inchannels
]);
161 fMidiSource
= new MIDIEndpointRef
[outchannels
+ fRealPlaybackChannels
];
165 for (int i
= 0; i
< outchannels
; i
++) {
166 std::stringstream num
;
168 str
= "JackMidi" + num
.str();
169 coutputStr
= CFStringCreateWithCString(0, str
.c_str(), CFStringGetSystemEncoding());
170 err
= MIDISourceCreate(fMidiClient
, coutputStr
, &fMidiSource
[i
]);
171 CFRelease(coutputStr
);
172 if (!fMidiSource
[i
]) {
173 jack_error("Cannot create CoreMidi source");
179 for (int i
= 0; i
< fRealPlaybackChannels
; i
++) {
180 fMidiSource
[i
+ outchannels
] = MIDIGetDestination(i
);
190 int JackCoreMidiDriver::Close()
193 MIDIPortDispose(fInputPort
);
196 MIDIPortDispose(fOutputPort
);
198 // Only dispose "virtual" endpoints
199 for (int i
= 0; i
< fCaptureChannels
- fRealCaptureChannels
; i
++) {
200 if (fMidiDestination
)
201 MIDIEndpointDispose(fMidiDestination
[i
]);
203 delete[] fMidiDestination
;
205 // Only dispose "virtual" endpoints
206 for (int i
= 0; i
< fPlaybackChannels
- fRealPlaybackChannels
; i
++) {
208 MIDIEndpointDispose(fMidiSource
[i
]);
210 delete[] fMidiSource
;
213 MIDIClientDispose(fMidiClient
);
218 int JackCoreMidiDriver::Attach()
223 jack_port_id_t port_index
;
224 char name
[JACK_CLIENT_NAME_SIZE
+ JACK_PORT_NAME_SIZE
];
225 char endpoint_name
[JACK_CLIENT_NAME_SIZE
+ JACK_PORT_NAME_SIZE
];
226 char alias
[JACK_CLIENT_NAME_SIZE
+ JACK_PORT_NAME_SIZE
];
227 unsigned long port_flags
= JackPortIsOutput
| JackPortIsPhysical
| JackPortIsTerminal
;
230 jack_log("JackCoreMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl
->fBufferSize
, fEngineControl
->fSampleRate
);
232 for (i
= 0; i
< fCaptureChannels
; i
++) {
234 err
= MIDIObjectGetStringProperty(fMidiDestination
[i
], kMIDIPropertyName
, &pname
);
236 CFStringGetCString(pname
, endpoint_name
, sizeof(endpoint_name
), 0);
238 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:out%d", fAliasName
, endpoint_name
, i
+ 1);
240 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:out%d", fAliasName
, fCaptureDriverName
, i
+ 1);
243 snprintf(name
, sizeof(name
) - 1, "%s:capture_%d", fClientControl
.fName
, i
+ 1);
244 if ((port_index
= fGraphManager
->AllocatePort(fClientControl
.fRefNum
, name
, JACK_DEFAULT_MIDI_TYPE
, (JackPortFlags
)port_flags
, fEngineControl
->fBufferSize
)) == NO_PORT
) {
245 jack_error("driver: cannot register port for %s", name
);
248 port
= fGraphManager
->GetPort(port_index
);
249 port
->SetAlias(alias
);
250 fCapturePortList
[i
] = port_index
;
251 jack_log("JackCoreMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index
);
254 port_flags
= JackPortIsInput
| JackPortIsPhysical
| JackPortIsTerminal
;
256 for (i
= 0; i
< fPlaybackChannels
; i
++) {
258 err
= MIDIObjectGetStringProperty(fMidiSource
[i
], kMIDIPropertyName
, &pname
);
260 CFStringGetCString(pname
, endpoint_name
, sizeof(endpoint_name
), 0);
262 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:in%d", fAliasName
, endpoint_name
, i
+ 1);
264 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:in%d", fAliasName
, fPlaybackDriverName
, i
+ 1);
267 snprintf(name
, sizeof(name
) - 1, "%s:playback_%d", fClientControl
.fName
, i
+ 1);
268 if ((port_index
= fGraphManager
->AllocatePort(fClientControl
.fRefNum
, name
, JACK_DEFAULT_MIDI_TYPE
, (JackPortFlags
)port_flags
, fEngineControl
->fBufferSize
)) == NO_PORT
) {
269 jack_error("driver: cannot register port for %s", name
);
272 port
= fGraphManager
->GetPort(port_index
);
273 port
->SetAlias(alias
);
274 fPlaybackPortList
[i
] = port_index
;
275 jack_log("JackCoreMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index
);
280 int JackCoreMidiDriver::Read()
282 for (int chan
= 0; chan
< fCaptureChannels
; chan
++) {
284 if (fGraphManager
->GetConnectionsNum(fCapturePortList
[chan
]) > 0) {
287 JackMidiBuffer
* midi_buffer
= GetInputBuffer(chan
);
289 if (jack_ringbuffer_read_space(fRingBuffer
[chan
]) == 0) {
291 midi_buffer
->Reset(midi_buffer
->nframes
);
294 while (jack_ringbuffer_read_space(fRingBuffer
[chan
]) > 0) {
298 jack_ringbuffer_read(fRingBuffer
[chan
], (char*)&ev_count
, sizeof(int));
300 for (int j
= 0; j
< ev_count
; j
++) {
303 jack_ringbuffer_read(fRingBuffer
[chan
], (char*)&event_len
, sizeof(UInt16
));
304 // Read event actual data
305 jack_midi_data_t
* dest
= midi_buffer
->ReserveEvent(0, event_len
);
306 jack_ringbuffer_read(fRingBuffer
[chan
], (char*)dest
, event_len
);
312 // Consume ring buffer
313 jack_ringbuffer_read_advance(fRingBuffer
[chan
], jack_ringbuffer_read_space(fRingBuffer
[chan
]));
319 int JackCoreMidiDriver::Write()
321 MIDIPacketList
* pktlist
= (MIDIPacketList
*)fMIDIBuffer
;
323 for (int chan
= 0; chan
< fPlaybackChannels
; chan
++) {
325 if (fGraphManager
->GetConnectionsNum(fPlaybackPortList
[chan
]) > 0) {
327 MIDIPacket
* packet
= MIDIPacketListInit(pktlist
);
328 JackMidiBuffer
* midi_buffer
= GetOutputBuffer(chan
);
330 // TODO : use timestamp
332 for (unsigned int j
= 0; j
< midi_buffer
->event_count
; j
++) {
333 JackMidiEvent
* ev
= &midi_buffer
->events
[j
];
334 packet
= MIDIPacketListAdd(pktlist
, sizeof(fMIDIBuffer
), packet
, MIDIGetCurrentHostTime(), ev
->size
, ev
->GetData(midi_buffer
));
338 if (chan
< fPlaybackChannels
- fRealPlaybackChannels
) {
339 OSStatus err
= MIDIReceived(fMidiSource
[chan
], pktlist
);
341 jack_error("MIDIReceived error");
343 OSStatus err
= MIDISend(fOutputPort
, fMidiSource
[chan
], pktlist
);
345 jack_error("MIDISend error");
354 } // end of namespace
361 SERVER_EXPORT jack_driver_desc_t
* driver_get_descriptor()
363 jack_driver_desc_t
* desc
;
366 desc
= (jack_driver_desc_t
*)calloc (1, sizeof (jack_driver_desc_t
));
367 strcpy(desc
->name
, "coremidi"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1
368 strcpy(desc
->desc
, "Apple CoreMIDI API based MIDI backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1
371 desc
->params
= (jack_driver_param_desc_t
*)calloc (desc
->nparams
, sizeof (jack_driver_param_desc_t
));
374 strcpy(desc
->params
[i
].name
, "inchannels");
375 desc
->params
[i
].character
= 'i';
376 desc
->params
[i
].type
= JackDriverParamInt
;
377 desc
->params
[i
].value
.ui
= 0;
378 strcpy(desc
->params
[i
].short_desc
, "CoreMIDI virtual bus");
379 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
382 strcpy(desc
->params
[i
].name
, "outchannels");
383 desc
->params
[i
].character
= 'o';
384 desc
->params
[i
].type
= JackDriverParamInt
;
385 desc
->params
[i
].value
.ui
= 0;
386 strcpy(desc
->params
[i
].short_desc
, "CoreMIDI virtual bus");
387 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
392 SERVER_EXPORT
Jack::JackDriverClientInterface
* driver_initialize(Jack::JackLockedEngine
* engine
, Jack::JackSynchro
* table
, const JSList
* params
)
395 const jack_driver_param_t
* param
;
399 for (node
= params
; node
; node
= jack_slist_next (node
)) {
400 param
= (const jack_driver_param_t
*) node
->data
;
402 switch (param
->character
) {
405 virtual_in
= param
->value
.ui
;
409 virtual_out
= param
->value
.ui
;
414 Jack::JackDriverClientInterface
* driver
= new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine
, table
);
415 if (driver
->Open(1, 1, virtual_in
, virtual_out
, false, "in", "out", 0, 0) == 0) {