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 // Check available size first..
56 size
= jack_ringbuffer_write_space(ringbuffer
);
57 if (size
< (sizeof(UInt16
) + packet
->length
)) {
58 jack_error("ReadProc : ring buffer is full, skip events...");
61 // Write length of each packet first
62 jack_ringbuffer_write(ringbuffer
, (char*)&packet
->length
, sizeof(UInt16
));
63 // Write event actual data
64 jack_ringbuffer_write(ringbuffer
, (char*)packet
->data
, packet
->length
);
66 packet
= MIDIPacketNext(packet
);
70 void JackCoreMidiDriver::ReadProc(const MIDIPacketList
*pktlist
, void *refCon
, void *connRefCon
)
72 jack_ringbuffer_t
* ringbuffer
= (jack_ringbuffer_t
*)connRefCon
;
73 ReadProcAux(pktlist
, ringbuffer
);
76 void JackCoreMidiDriver::ReadVirtualProc(const MIDIPacketList
*pktlist
, void *refCon
, void *connRefCon
)
78 jack_ringbuffer_t
* ringbuffer
= (jack_ringbuffer_t
*)refCon
;
79 ReadProcAux(pktlist
, ringbuffer
);
82 void JackCoreMidiDriver::NotifyProc(const MIDINotification
*message
, void *refCon
)
84 jack_info("NotifyProc %d", message
->messageID
);
87 JackCoreMidiDriver::JackCoreMidiDriver(const char* name
, const char* alias
, JackLockedEngine
* engine
, JackSynchro
* table
)
88 : JackMidiDriver(name
, alias
, engine
, table
), fMidiClient(NULL
), fInputPort(NULL
), fOutputPort(NULL
), fRealCaptureChannels(0), fRealPlaybackChannels(0)
91 JackCoreMidiDriver::~JackCoreMidiDriver()
94 int JackCoreMidiDriver::Open(bool capturing
,
99 const char* capture_driver_name
,
100 const char* playback_driver_name
,
101 jack_nframes_t capture_latency
,
102 jack_nframes_t playback_latency
)
105 CFStringRef coutputStr
;
108 // Get real input/output number
109 fRealCaptureChannels
= MIDIGetNumberOfSources();
110 fRealPlaybackChannels
= MIDIGetNumberOfDestinations();
112 // Generic JackMidiDriver Open
113 if (JackMidiDriver::Open(capturing
, playing
, inchannels
+ fRealCaptureChannels
, outchannels
+ fRealPlaybackChannels
, monitor
, capture_driver_name
, playback_driver_name
, capture_latency
, playback_latency
) != 0)
116 coutputStr
= CFStringCreateWithCString(0, "JackMidi", CFStringGetSystemEncoding());
117 err
= MIDIClientCreate(coutputStr
, NotifyProc
, this, &fMidiClient
);
118 CFRelease(coutputStr
);
120 jack_error("Cannot create CoreMidi client");
124 err
= MIDIInputPortCreate(fMidiClient
, CFSTR("Input port"), ReadProc
, this, &fInputPort
);
126 jack_error("Cannot open CoreMidi in port\n");
130 err
= MIDIOutputPortCreate(fMidiClient
, CFSTR("Output port"), &fOutputPort
);
132 jack_error("Cannot open CoreMidi out port\n");
136 fMidiDestination
= new MIDIEndpointRef
[inchannels
+ fRealCaptureChannels
];
137 assert(fMidiDestination
);
140 for (int i
= 0; i
< inchannels
; i
++) {
141 std::stringstream num
;
143 str
= "JackMidi" + num
.str();
144 coutputStr
= CFStringCreateWithCString(0, str
.c_str(), CFStringGetSystemEncoding());
145 err
= MIDIDestinationCreate(fMidiClient
, coutputStr
, ReadVirtualProc
, fRingBuffer
[i
], &fMidiDestination
[i
]);
146 CFRelease(coutputStr
);
147 if (!fMidiDestination
[i
]) {
148 jack_error("Cannot create CoreMidi destination");
154 for (int i
= 0; i
< fRealCaptureChannels
; i
++) {
155 fMidiDestination
[i
+ inchannels
] = MIDIGetSource(i
);
156 MIDIPortConnectSource(fInputPort
, fMidiDestination
[i
+ inchannels
], fRingBuffer
[i
+ inchannels
]);
159 fMidiSource
= new MIDIEndpointRef
[outchannels
+ fRealPlaybackChannels
];
163 for (int i
= 0; i
< outchannels
; i
++) {
164 std::stringstream num
;
166 str
= "JackMidi" + num
.str();
167 coutputStr
= CFStringCreateWithCString(0, str
.c_str(), CFStringGetSystemEncoding());
168 err
= MIDISourceCreate(fMidiClient
, coutputStr
, &fMidiSource
[i
]);
169 CFRelease(coutputStr
);
170 if (!fMidiSource
[i
]) {
171 jack_error("Cannot create CoreMidi source");
177 for (int i
= 0; i
< fRealPlaybackChannels
; i
++) {
178 fMidiSource
[i
+ outchannels
] = MIDIGetDestination(i
);
188 int JackCoreMidiDriver::Close()
191 MIDIPortDispose(fInputPort
);
194 MIDIPortDispose(fOutputPort
);
196 // Only dispose "virtual" endpoints
197 for (int i
= 0; i
< fCaptureChannels
- fRealCaptureChannels
; i
++) {
198 if (fMidiDestination
)
199 MIDIEndpointDispose(fMidiDestination
[i
]);
201 delete[] fMidiDestination
;
203 // Only dispose "virtual" endpoints
204 for (int i
= 0; i
< fPlaybackChannels
- fRealPlaybackChannels
; i
++) {
206 MIDIEndpointDispose(fMidiSource
[i
]);
208 delete[] fMidiSource
;
211 MIDIClientDispose(fMidiClient
);
216 int JackCoreMidiDriver::Attach()
221 jack_port_id_t port_index
;
222 char name
[JACK_CLIENT_NAME_SIZE
+ JACK_PORT_NAME_SIZE
];
223 char endpoint_name
[JACK_CLIENT_NAME_SIZE
+ JACK_PORT_NAME_SIZE
];
224 char alias
[JACK_CLIENT_NAME_SIZE
+ JACK_PORT_NAME_SIZE
];
227 jack_log("JackCoreMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl
->fBufferSize
, fEngineControl
->fSampleRate
);
229 for (i
= 0; i
< fCaptureChannels
; i
++) {
231 err
= MIDIObjectGetStringProperty(fMidiDestination
[i
], kMIDIPropertyName
, &pname
);
233 CFStringGetCString(pname
, endpoint_name
, sizeof(endpoint_name
), 0);
235 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:out%d", fAliasName
, endpoint_name
, i
+ 1);
237 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:out%d", fAliasName
, fCaptureDriverName
, i
+ 1);
240 snprintf(name
, sizeof(name
) - 1, "%s:capture_%d", fClientControl
.fName
, i
+ 1);
241 if ((port_index
= fGraphManager
->AllocatePort(fClientControl
.fRefNum
, name
, JACK_DEFAULT_MIDI_TYPE
, CaptureDriverFlags
, fEngineControl
->fBufferSize
)) == NO_PORT
) {
242 jack_error("driver: cannot register port for %s", name
);
245 port
= fGraphManager
->GetPort(port_index
);
246 port
->SetAlias(alias
);
247 fCapturePortList
[i
] = port_index
;
248 jack_log("JackCoreMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index
);
251 for (i
= 0; i
< fPlaybackChannels
; i
++) {
253 err
= MIDIObjectGetStringProperty(fMidiSource
[i
], kMIDIPropertyName
, &pname
);
255 CFStringGetCString(pname
, endpoint_name
, sizeof(endpoint_name
), 0);
257 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:in%d", fAliasName
, endpoint_name
, i
+ 1);
259 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:in%d", fAliasName
, fPlaybackDriverName
, i
+ 1);
262 snprintf(name
, sizeof(name
) - 1, "%s:playback_%d", fClientControl
.fName
, i
+ 1);
263 if ((port_index
= fGraphManager
->AllocatePort(fClientControl
.fRefNum
, name
, JACK_DEFAULT_MIDI_TYPE
, PlaybackDriverFlags
, fEngineControl
->fBufferSize
)) == NO_PORT
) {
264 jack_error("driver: cannot register port for %s", name
);
267 port
= fGraphManager
->GetPort(port_index
);
268 port
->SetAlias(alias
);
269 fPlaybackPortList
[i
] = port_index
;
270 jack_log("JackCoreMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index
);
275 int JackCoreMidiDriver::Read()
277 for (int chan
= 0; chan
< fCaptureChannels
; chan
++) {
279 if (fGraphManager
->GetConnectionsNum(fCapturePortList
[chan
]) > 0) {
282 JackMidiBuffer
* midi_buffer
= GetInputBuffer(chan
);
284 if (jack_ringbuffer_read_space(fRingBuffer
[chan
]) == 0) {
286 midi_buffer
->Reset(midi_buffer
->nframes
);
289 while (jack_ringbuffer_read_space(fRingBuffer
[chan
]) > 0) {
293 jack_ringbuffer_read(fRingBuffer
[chan
], (char*)&ev_count
, sizeof(int));
295 for (int j
= 0; j
< ev_count
; j
++) {
298 jack_ringbuffer_read(fRingBuffer
[chan
], (char*)&event_len
, sizeof(UInt16
));
299 // Read event actual data
300 jack_midi_data_t
* dest
= midi_buffer
->ReserveEvent(0, event_len
);
301 jack_ringbuffer_read(fRingBuffer
[chan
], (char*)dest
, event_len
);
307 // Consume ring buffer
308 jack_ringbuffer_read_advance(fRingBuffer
[chan
], jack_ringbuffer_read_space(fRingBuffer
[chan
]));
314 int JackCoreMidiDriver::Write()
316 MIDIPacketList
* pktlist
= (MIDIPacketList
*)fMIDIBuffer
;
318 for (int chan
= 0; chan
< fPlaybackChannels
; chan
++) {
320 if (fGraphManager
->GetConnectionsNum(fPlaybackPortList
[chan
]) > 0) {
322 MIDIPacket
* packet
= MIDIPacketListInit(pktlist
);
323 JackMidiBuffer
* midi_buffer
= GetOutputBuffer(chan
);
325 // TODO : use timestamp
327 for (unsigned int j
= 0; j
< midi_buffer
->event_count
; j
++) {
328 JackMidiEvent
* ev
= &midi_buffer
->events
[j
];
329 packet
= MIDIPacketListAdd(pktlist
, sizeof(fMIDIBuffer
), packet
, MIDIGetCurrentHostTime(), ev
->size
, ev
->GetData(midi_buffer
));
333 if (chan
< fPlaybackChannels
- fRealPlaybackChannels
) {
334 OSStatus err
= MIDIReceived(fMidiSource
[chan
], pktlist
);
336 jack_error("MIDIReceived error");
338 OSStatus err
= MIDISend(fOutputPort
, fMidiSource
[chan
], pktlist
);
340 jack_error("MIDISend error");
349 } // end of namespace
356 SERVER_EXPORT jack_driver_desc_t
* driver_get_descriptor()
358 jack_driver_desc_t
* desc
;
361 desc
= (jack_driver_desc_t
*)calloc (1, sizeof (jack_driver_desc_t
));
362 strcpy(desc
->name
, "coremidi"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1
363 strcpy(desc
->desc
, "Apple CoreMIDI API based MIDI backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1
366 desc
->params
= (jack_driver_param_desc_t
*)calloc (desc
->nparams
, sizeof (jack_driver_param_desc_t
));
369 strcpy(desc
->params
[i
].name
, "inchannels");
370 desc
->params
[i
].character
= 'i';
371 desc
->params
[i
].type
= JackDriverParamInt
;
372 desc
->params
[i
].value
.ui
= 0;
373 strcpy(desc
->params
[i
].short_desc
, "CoreMIDI virtual bus");
374 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
377 strcpy(desc
->params
[i
].name
, "outchannels");
378 desc
->params
[i
].character
= 'o';
379 desc
->params
[i
].type
= JackDriverParamInt
;
380 desc
->params
[i
].value
.ui
= 0;
381 strcpy(desc
->params
[i
].short_desc
, "CoreMIDI virtual bus");
382 strcpy(desc
->params
[i
].long_desc
, desc
->params
[i
].short_desc
);
387 SERVER_EXPORT
Jack::JackDriverClientInterface
* driver_initialize(Jack::JackLockedEngine
* engine
, Jack::JackSynchro
* table
, const JSList
* params
)
390 const jack_driver_param_t
* param
;
394 for (node
= params
; node
; node
= jack_slist_next (node
)) {
395 param
= (const jack_driver_param_t
*) node
->data
;
397 switch (param
->character
) {
400 virtual_in
= param
->value
.ui
;
404 virtual_out
= param
->value
.ui
;
409 Jack::JackDriverClientInterface
* driver
= new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine
, table
);
410 if (driver
->Open(1, 1, virtual_in
, virtual_out
, false, "in", "out", 0, 0) == 0) {