/* * Platform interface to the MacOS X CoreMIDI framework * * Jon Parise * * $Id: pmmacosx.c,v 1.1.1.1 2003-05-09 16:04:00 ggeiger Exp $ */ #include "portmidi.h" #include "pminternal.h" #include "porttime.h" #include "pmmacosx.h" #include #include #include #include #define PACKET_BUFFER_SIZE 1024 static MIDIClientRef client = NULL; /* Client handle to the MIDI server */ static MIDIPortRef portIn = NULL; /* Input port handle */ static MIDIPortRef portOut = NULL; /* Output port handle */ extern pm_fns_node pm_macosx_in_dictionary; extern pm_fns_node pm_macosx_out_dictionary; static int midi_length(long msg) { int status, high, low; static int high_lengths[] = { 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ }; static int low_lengths[] = { 1, 1, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ }; status = msg & 0xFF; high = status >> 4; low = status & 15; return (high != 0xF0) ? high_lengths[high] : low_lengths[low]; } static PmTimestamp get_timestamp(PmInternal *midi) { PmTimeProcPtr time_proc; /* Set the time procedure accordingly */ time_proc = midi->time_proc; if (time_proc == NULL) { time_proc = Pt_Time; } return (*time_proc)(midi->time_info); } /* called when MIDI packets are received */ static void readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon) { PmInternal *midi; PmEvent event; MIDIPacket *packet; unsigned int packetIndex; /* Retrieve the context for this connection */ midi = (PmInternal *) connRefCon; packet = (MIDIPacket *) &newPackets->packet[0]; for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) { /* Build the PmMessage for the PmEvent structure */ switch (packet->length) { case 1: event.message = Pm_Message(packet->data[0], 0, 0); break; case 2: event.message = Pm_Message(packet->data[0], packet->data[1], 0); break; case 3: event.message = Pm_Message(packet->data[0], packet->data[1], packet->data[2]); break; default: /* Skip packets that are too large to fit in a PmMessage */ continue; } /* Set the timestamp and dispatch this message */ event.timestamp = get_timestamp(midi); pm_enqueue(midi, &event); /* Advance to the next packet in the packet list */ packet = MIDIPacketNext(packet); } } static PmError midi_in_open(PmInternal *midi, void *driverInfo) { MIDIEndpointRef endpoint; endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; if (endpoint == NULL) { return pmInvalidDeviceId; } if (MIDIPortConnectSource(portIn, endpoint, midi) != noErr) { return pmHostError; } return pmNoError; } static PmError midi_in_close(PmInternal *midi) { MIDIEndpointRef endpoint; endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; if (endpoint == NULL) { return pmInvalidDeviceId; } if (MIDIPortDisconnectSource(portIn, endpoint) != noErr) { return pmHostError; } return pmNoError; } static PmError midi_out_open(PmInternal *midi, void *driverInfo) { /* * MIDISent() only requires an output port (portOut) and a valid MIDI * endpoint (which we've already created and stored in the PmInternal * structure). Therefore, no additional work needs to be done here to * open the device for output. */ return pmNoError; } static PmError midi_out_close(PmInternal *midi) { return pmNoError; } static PmError midi_abort(PmInternal *midi) { return pmNoError; } static PmError midi_write(PmInternal *midi, PmEvent *events, long length) { Byte packetBuffer[PACKET_BUFFER_SIZE]; MIDIEndpointRef endpoint; MIDIPacketList *packetList; MIDIPacket *packet; MIDITimeStamp timestamp; PmTimeProcPtr time_proc; PmEvent event; unsigned int pm_time; unsigned int eventIndex; unsigned int messageLength; Byte message[3]; endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; if (endpoint == NULL) { return pmInvalidDeviceId; } /* Make sure the packetBuffer is large enough */ if (length > PACKET_BUFFER_SIZE) { return pmHostError; } /* * Initialize the packet list. Each packet contains bytes that are to * be played at the same time. */ packetList = (MIDIPacketList *) packetBuffer; if ((packet = MIDIPacketListInit(packetList)) == NULL) { return pmHostError; } /* Set the time procedure accordingly */ time_proc = midi->time_proc; if (time_proc == NULL) { time_proc = Pt_Time; } /* Extract the event data and pack it into the message buffer */ for (eventIndex = 0; eventIndex < length; eventIndex++) { event = events[eventIndex]; /* Compute the timestamp */ pm_time = (*time_proc)(midi->time_info); timestamp = pm_time + midi->latency; messageLength = midi_length(event.message); message[0] = Pm_MessageStatus(event.message); message[1] = Pm_MessageData1(event.message); message[2] = Pm_MessageData2(event.message); /* Add this message to the packet list */ packet = MIDIPacketListAdd(packetList, sizeof(packetBuffer), packet, timestamp, messageLength, message); if (packet == NULL) { return pmHostError; } } if (MIDISend(portOut, endpoint, packetList) != noErr) { return pmHostError; } return pmNoError; } pm_fns_node pm_macosx_in_dictionary = { none_write, midi_in_open, midi_abort, midi_in_close }; pm_fns_node pm_macosx_out_dictionary = { midi_write, midi_out_open, midi_abort, midi_out_close }; PmError pm_macosx_init(void) { OSStatus status; ItemCount numDevices, numInputs, numOutputs; MIDIEndpointRef endpoint; CFStringEncoding defaultEncoding; CFStringRef deviceName; char nameBuf[256]; int i; /* Determine the number of MIDI devices on the system */ numDevices = MIDIGetNumberOfDevices(); numInputs = MIDIGetNumberOfSources(); numOutputs = MIDIGetNumberOfDestinations(); /* Return prematurely if no devices exist on the system */ if (numDevices <= 0) { return pmHostError; } /* Determine the default system character encording */ defaultEncoding = CFStringGetSystemEncoding(); /* Iterate over the MIDI input devices */ for (i = 0; i < numInputs; i++) { endpoint = MIDIGetSource(i); if (endpoint == NULL) { continue; } /* Get the name of this device */ MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &deviceName); CFStringGetCString(deviceName, nameBuf, 256, defaultEncoding); CFRelease(deviceName); /* Register this device with PortMidi */ pm_add_device("CoreMIDI", nameBuf, TRUE, (void *)endpoint, &pm_macosx_in_dictionary); } /* Iterate over the MIDI output devices */ for (i = 0; i < numOutputs; i++) { endpoint = MIDIGetDestination(i); if (endpoint == NULL) { continue; } /* Get the name of this device */ MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &deviceName); CFStringGetCString(deviceName, nameBuf, 256, defaultEncoding); CFRelease(deviceName); /* Register this device with PortMidi */ pm_add_device("CoreMIDI", nameBuf, FALSE, (void *)endpoint, &pm_macosx_out_dictionary); } /* Initialize the client handle */ status = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client); if (status != noErr) { fprintf(stderr, "Could not initialize client: %d\n", (int)status); return pmHostError; } /* Create the input port */ status = MIDIInputPortCreate(client, CFSTR("Input port"), readProc, NULL, &portIn); if (status != noErr) { fprintf(stderr, "Could not create input port: %d\n", (int)status); return pmHostError; } /* Create the output port */ status = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut); if (status != noErr) { fprintf(stderr, "Could not create output port: %d\n", (int)status); return pmHostError; } return pmNoError; } PmError pm_macosx_term(void) { if (client != NULL) MIDIClientDispose(client); if (portIn != NULL) MIDIPortDispose(portIn); if (portOut != NULL) MIDIPortDispose(portOut); return pmNoError; }