Cleanup wscripts.
[jack2.git] / macosx / coremidi / JackCoreMidiDriver.cpp
blob891af946ba7b58a7f5e78456daf2dfca92cdaefb
1 /*
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>
27 #include <assert.h>
28 #include <iostream>
29 #include <sstream>
30 #include <string>
32 namespace Jack
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...");
46 return;
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...");
59 return;
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...");
65 return;
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,
97 bool playing,
98 int inchannels,
99 int outchannels,
100 bool monitor,
101 const char* capture_driver_name,
102 const char* playback_driver_name,
103 jack_nframes_t capture_latency,
104 jack_nframes_t playback_latency)
106 OSStatus err;
107 CFStringRef coutputStr;
108 std::string str;
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)
116 return -1;
118 coutputStr = CFStringCreateWithCString(0, "JackMidi", CFStringGetSystemEncoding());
119 err = MIDIClientCreate(coutputStr, NotifyProc, this, &fMidiClient);
120 CFRelease(coutputStr);
121 if (!fMidiClient) {
122 jack_error("Cannot create CoreMidi client");
123 goto error;
126 err = MIDIInputPortCreate(fMidiClient, CFSTR("Input port"), ReadProc, this, &fInputPort);
127 if (!fInputPort) {
128 jack_error("Cannot open CoreMidi in port\n");
129 goto error;
132 err = MIDIOutputPortCreate(fMidiClient, CFSTR("Output port"), &fOutputPort);
133 if (!fOutputPort) {
134 jack_error("Cannot open CoreMidi out port\n");
135 goto error;
138 fMidiDestination = new MIDIEndpointRef[inchannels + fRealCaptureChannels];
139 assert(fMidiDestination);
141 // Virtual input
142 for (int i = 0; i < inchannels; i++) {
143 std::stringstream num;
144 num << i;
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");
151 goto error;
155 // Real input
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];
162 assert(fMidiSource);
164 // Virtual output
165 for (int i = 0; i < outchannels; i++) {
166 std::stringstream num;
167 num << i;
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");
174 goto error;
178 // Real output
179 for (int i = 0; i < fRealPlaybackChannels; i++) {
180 fMidiSource[i + outchannels] = MIDIGetDestination(i);
183 return 0;
185 error:
186 Close();
187 return -1;
190 int JackCoreMidiDriver::Close()
192 if (fInputPort)
193 MIDIPortDispose(fInputPort);
195 if (fOutputPort)
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++) {
207 if (fMidiSource[i])
208 MIDIEndpointDispose(fMidiSource[i]);
210 delete[] fMidiSource;
212 if (fMidiClient)
213 MIDIClientDispose(fMidiClient);
215 return 0;
218 int JackCoreMidiDriver::Attach()
220 OSStatus err;
221 JackPort* port;
222 CFStringRef pname;
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;
228 int i;
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);
235 if (err == noErr) {
236 CFStringGetCString(pname, endpoint_name, sizeof(endpoint_name), 0);
237 CFRelease(pname);
238 snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, endpoint_name, i + 1);
239 } else {
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);
246 return -1;
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);
259 if (err == noErr) {
260 CFStringGetCString(pname, endpoint_name, sizeof(endpoint_name), 0);
261 CFRelease(pname);
262 snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName, endpoint_name, i + 1);
263 } else {
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);
270 return -1;
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);
278 return 0;
280 int JackCoreMidiDriver::Read()
282 for (int chan = 0; chan < fCaptureChannels; chan++) {
284 if (fGraphManager->GetConnectionsNum(fCapturePortList[chan]) > 0) {
286 // Get JACK port
287 JackMidiBuffer* midi_buffer = GetInputBuffer(chan);
289 if (jack_ringbuffer_read_space(fRingBuffer[chan]) == 0) {
290 // Reset buffer
291 midi_buffer->Reset(midi_buffer->nframes);
292 } else {
294 while (jack_ringbuffer_read_space(fRingBuffer[chan]) > 0) {
296 // Read event number
297 int ev_count = 0;
298 jack_ringbuffer_read(fRingBuffer[chan], (char*)&ev_count, sizeof(int));
300 for (int j = 0; j < ev_count; j++) {
301 // Read event length
302 UInt16 event_len;
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);
311 } else {
312 // Consume ring buffer
313 jack_ringbuffer_read_advance(fRingBuffer[chan], jack_ringbuffer_read_space(fRingBuffer[chan]));
316 return 0;
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));
337 if (packet) {
338 if (chan < fPlaybackChannels - fRealPlaybackChannels) {
339 OSStatus err = MIDIReceived(fMidiSource[chan], pktlist);
340 if (err != noErr)
341 jack_error("MIDIReceived error");
342 } else {
343 OSStatus err = MIDISend(fOutputPort, fMidiSource[chan], pktlist);
344 if (err != noErr)
345 jack_error("MIDISend error");
351 return 0;
354 } // end of namespace
356 #ifdef __cplusplus
357 extern "C"
359 #endif
361 SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor()
363 jack_driver_desc_t * desc;
364 unsigned int i;
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
370 desc->nparams = 2;
371 desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t));
373 i = 0;
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);
381 i++;
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);
389 return desc;
392 SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
394 const JSList * node;
395 const jack_driver_param_t * param;
396 int virtual_in = 0;
397 int virtual_out = 0;
399 for (node = params; node; node = jack_slist_next (node)) {
400 param = (const jack_driver_param_t *) node->data;
402 switch (param->character) {
404 case 'i':
405 virtual_in = param->value.ui;
406 break;
408 case 'o':
409 virtual_out = param->value.ui;
410 break;
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) {
416 return driver;
417 } else {
418 delete driver;
419 return NULL;
423 #ifdef __cplusplus
425 #endif