lms-video/pd-0.44-2/portmidi/pm_mac/pmmacosxcm.c
Santi Noreña e85d191b46 - Reestructuración de ficheros y directorios general
- merge v0.01 --> Añadido fileselector
- Añadidas fuentes de Gem y Pure Data
- pix2jpg incluído en Gem. Archivos de construcción de Gem modificados.
- Añadido fichero ompiling.txt con instrucciones de compilación
2013-02-04 18:00:17 +01:00

934 lines
29 KiB
C
Executable file

/*
* Platform interface to the MacOS X CoreMIDI framework
*
* Jon Parise <jparise at cmu.edu>
* and subsequent work by Andrew Zeldis and Zico Kolter
* and Roger B. Dannenberg
*
* $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $
*/
/* Notes:
since the input and output streams are represented by MIDIEndpointRef
values and almost no other state, we store the MIDIEndpointRef on
descriptors[midi->device_id].descriptor. The only other state we need
is for errors: we need to know if there is an error and if so, what is
the error text. We use a structure with two kinds of
host error: "error" and "callback_error". That way, asynchronous callbacks
do not interfere with other error information.
OS X does not seem to have an error-code-to-text function, so we will
just use text messages instead of error codes.
*/
#include <stdlib.h>
//#define CM_DEBUG 1
#include "portmidi.h"
#ifdef NEWBUFFER
#include "pmutil.h"
#endif
#include "pminternal.h"
#include "porttime.h"
#include "pmmac.h"
#include "pmmacosxcm.h"
#include <stdio.h>
#include <string.h>
#include <CoreServices/CoreServices.h>
#include <CoreMIDI/MIDIServices.h>
#include <CoreAudio/HostTime.h>
#define PACKET_BUFFER_SIZE 1024
/* this is very strange: if I put in a reasonable
number here, e.g. 128, which would allow sysex data
to be sent 128 bytes at a time, then I lose sysex
data in my loopback test. With a buffer size of 4,
we put at most 4 bytes in a packet (but maybe many
packets in a packetList), and everything works fine.
*/
#define SYSEX_BUFFER_SIZE 4
#define VERBOSE_ON 1
#define VERBOSE if (VERBOSE_ON)
#define MIDI_SYSEX 0xf0
#define MIDI_EOX 0xf7
#define MIDI_STATUS_MASK 0x80
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;
typedef struct midi_macosxcm_struct {
unsigned long sync_time; /* when did we last determine delta? */
UInt64 delta; /* difference between stream time and real time in ns */
UInt64 last_time; /* last output time */
int first_message; /* tells midi_write to sychronize timestamps */
int sysex_mode; /* middle of sending sysex */
unsigned long sysex_word; /* accumulate data when receiving sysex */
unsigned int sysex_byte_count; /* count how many received */
char error[PM_HOST_ERROR_MSG_LEN];
char callback_error[PM_HOST_ERROR_MSG_LEN];
Byte packetBuffer[PACKET_BUFFER_SIZE];
MIDIPacketList *packetList; /* a pointer to packetBuffer */
MIDIPacket *packet;
Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */
MIDITimeStamp sysex_timestamp; /* timestamp to use with sysex data */
/* allow for running status (is running status possible here? -rbd): -cpr */
unsigned char last_command;
long last_msg_length;
} midi_macosxcm_node, *midi_macosxcm_type;
/* private function declarations */
MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp);
PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp);
char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint);
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 midi_synchronize(PmInternal *midi)
{
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
UInt64 pm_stream_time_2 =
AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
PmTimestamp real_time;
UInt64 pm_stream_time;
/* if latency is zero and this is an output, there is no
time reference and midi_synchronize should never be called */
assert(midi->time_proc);
assert(!(midi->write_flag && midi->latency == 0));
do {
/* read real_time between two reads of stream time */
pm_stream_time = pm_stream_time_2;
real_time = (*midi->time_proc)(midi->time_info);
pm_stream_time_2 = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
/* repeat if more than 0.5 ms has elapsed */
} while (pm_stream_time_2 > pm_stream_time + 500000);
m->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000);
m->sync_time = real_time;
return real_time;
}
static void
process_packet(MIDIPacket *packet, PmEvent *event,
PmInternal *midi, midi_macosxcm_type m)
{
/* handle a packet of MIDI messages from CoreMIDI */
/* there may be multiple short messages in one packet (!) */
unsigned int remaining_length = packet->length;
unsigned char *cur_packet_data = packet->data;
while (remaining_length > 0) {
if (cur_packet_data[0] == MIDI_SYSEX ||
/* are we in the middle of a sysex message? */
(m->last_command == 0 &&
!(cur_packet_data[0] & MIDI_STATUS_MASK))) {
m->last_command = 0; /* no running status */
unsigned int amt = pm_read_bytes(midi, cur_packet_data,
remaining_length,
event->timestamp);
remaining_length -= amt;
cur_packet_data += amt;
} else if (cur_packet_data[0] == MIDI_EOX) {
/* this should never happen, because pm_read_bytes should
* get and read all EOX bytes*/
midi->sysex_in_progress = FALSE;
m->last_command = 0;
} else if (cur_packet_data[0] & MIDI_STATUS_MASK) {
/* compute the length of the next (short) msg in packet */
unsigned int cur_message_length = midi_length(cur_packet_data[0]);
if (cur_message_length > remaining_length) {
#ifdef DEBUG
printf("PortMidi debug msg: not enough data");
#endif
/* since there's no more data, we're done */
return;
}
m->last_msg_length = cur_message_length;
m->last_command = cur_packet_data[0];
switch (cur_message_length) {
case 1:
event->message = Pm_Message(cur_packet_data[0], 0, 0);
break;
case 2:
event->message = Pm_Message(cur_packet_data[0],
cur_packet_data[1], 0);
break;
case 3:
event->message = Pm_Message(cur_packet_data[0],
cur_packet_data[1],
cur_packet_data[2]);
break;
default:
/* PortMIDI internal error; should never happen */
assert(cur_message_length == 1);
return; /* give up on packet if continued after assert */
}
pm_read_short(midi, event);
remaining_length -= m->last_msg_length;
cur_packet_data += m->last_msg_length;
} else if (m->last_msg_length > remaining_length + 1) {
/* we have running status, but not enough data */
#ifdef DEBUG
printf("PortMidi debug msg: not enough data in CoreMIDI packet");
#endif
/* since there's no more data, we're done */
return;
} else { /* output message using running status */
switch (m->last_msg_length) {
case 1:
event->message = Pm_Message(m->last_command, 0, 0);
break;
case 2:
event->message = Pm_Message(m->last_command,
cur_packet_data[0], 0);
break;
case 3:
event->message = Pm_Message(m->last_command,
cur_packet_data[0],
cur_packet_data[1]);
break;
default:
/* last_msg_length is invalid -- internal PortMIDI error */
assert(m->last_msg_length == 1);
}
pm_read_short(midi, event);
remaining_length -= (m->last_msg_length - 1);
cur_packet_data += (m->last_msg_length - 1);
}
}
}
/* called when MIDI packets are received */
static void
readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon)
{
PmInternal *midi;
midi_macosxcm_type m;
PmEvent event;
MIDIPacket *packet;
unsigned int packetIndex;
unsigned long now;
unsigned int status;
#ifdef CM_DEBUG
printf("readProc: numPackets %d: ", newPackets->numPackets);
#endif
/* Retrieve the context for this connection */
midi = (PmInternal *) connRefCon;
m = (midi_macosxcm_type) midi->descriptor;
assert(m);
/* synchronize time references every 100ms */
now = (*midi->time_proc)(midi->time_info);
if (m->first_message || m->sync_time + 100 /*ms*/ < now) {
/* time to resync */
now = midi_synchronize(midi);
m->first_message = FALSE;
}
packet = (MIDIPacket *) &newPackets->packet[0];
/* printf("readproc packet status %x length %d\n", packet->data[0],
packet->length); */
for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) {
/* Set the timestamp and dispatch this message */
event.timestamp =
(AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) /
(UInt64) 1000000;
status = packet->data[0];
/* process packet as sysex data if it begins with MIDI_SYSEX, or
MIDI_EOX or non-status byte with no running status */
#ifdef CM_DEBUG
printf(" %d", packet->length);
#endif
if (status == MIDI_SYSEX || status == MIDI_EOX ||
((!(status & MIDI_STATUS_MASK)) && !m->last_command)) {
/* previously was: !(status & MIDI_STATUS_MASK)) {
* but this could mistake running status for sysex data
*/
/* reset running status data -cpr */
m->last_command = 0;
m->last_msg_length = 0;
/* printf("sysex packet length: %d\n", packet->length); */
pm_read_bytes(midi, packet->data, packet->length, event.timestamp);
} else {
process_packet(packet, &event, midi, m);
}
packet = MIDIPacketNext(packet);
}
#ifdef CM_DEBUG
printf("\n");
#endif
}
static PmError
midi_in_open(PmInternal *midi, void *driverInfo)
{
MIDIEndpointRef endpoint;
midi_macosxcm_type m;
OSStatus macHostError;
/* insure that we have a time_proc for timing */
if (midi->time_proc == NULL) {
if (!Pt_Started())
Pt_Start(1, 0, 0);
/* time_get does not take a parameter, so coerce */
midi->time_proc = (PmTimeProcPtr) Pt_Time;
}
endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
if (endpoint == NULL) {
return pmInvalidDeviceId;
}
m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
midi->descriptor = m;
if (!m) {
return pmInsufficientMemory;
}
m->error[0] = 0;
m->callback_error[0] = 0;
m->sync_time = 0;
m->delta = 0;
m->last_time = 0;
m->first_message = TRUE;
m->sysex_mode = FALSE;
m->sysex_word = 0;
m->sysex_byte_count = 0;
m->packetList = NULL;
m->packet = NULL;
m->last_command = 0;
m->last_msg_length = 0;
macHostError = MIDIPortConnectSource(portIn, endpoint, midi);
if (macHostError != noErr) {
pm_hosterror = macHostError;
sprintf(pm_hosterror_text,
"Host error %ld: MIDIPortConnectSource() in midi_in_open()",
macHostError);
midi->descriptor = NULL;
pm_free(m);
return pmHostError;
}
return pmNoError;
}
static PmError
midi_in_close(PmInternal *midi)
{
MIDIEndpointRef endpoint;
OSStatus macHostError;
PmError err = pmNoError;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
if (!m) return pmBadPtr;
endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
if (endpoint == NULL) {
pm_hosterror = pmBadPtr;
}
/* shut off the incoming messages before freeing data structures */
macHostError = MIDIPortDisconnectSource(portIn, endpoint);
if (macHostError != noErr) {
pm_hosterror = macHostError;
sprintf(pm_hosterror_text,
"Host error %ld: MIDIPortDisconnectSource() in midi_in_close()",
macHostError);
err = pmHostError;
}
midi->descriptor = NULL;
pm_free(midi->descriptor);
return err;
}
static PmError
midi_out_open(PmInternal *midi, void *driverInfo)
{
midi_macosxcm_type m;
m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
midi->descriptor = m;
if (!m) {
return pmInsufficientMemory;
}
m->error[0] = 0;
m->callback_error[0] = 0;
m->sync_time = 0;
m->delta = 0;
m->last_time = 0;
m->first_message = TRUE;
m->sysex_mode = FALSE;
m->sysex_word = 0;
m->sysex_byte_count = 0;
m->packetList = (MIDIPacketList *) m->packetBuffer;
m->packet = NULL;
m->last_command = 0;
m->last_msg_length = 0;
return pmNoError;
}
static PmError
midi_out_close(PmInternal *midi)
{
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
if (!m) return pmBadPtr;
midi->descriptor = NULL;
pm_free(midi->descriptor);
return pmNoError;
}
static PmError
midi_abort(PmInternal *midi)
{
return pmNoError;
}
static PmError
midi_write_flush(PmInternal *midi, PmTimestamp timestamp)
{
OSStatus macHostError;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
MIDIEndpointRef endpoint =
(MIDIEndpointRef) descriptors[midi->device_id].descriptor;
assert(m);
assert(endpoint);
if (m->packet != NULL) {
/* out of space, send the buffer and start refilling it */
macHostError = MIDISend(portOut, endpoint, m->packetList);
m->packet = NULL; /* indicate no data in packetList now */
if (macHostError != noErr) goto send_packet_error;
}
return pmNoError;
send_packet_error:
pm_hosterror = macHostError;
sprintf(pm_hosterror_text,
"Host error %ld: MIDISend() in midi_write()",
macHostError);
return pmHostError;
}
static PmError
send_packet(PmInternal *midi, Byte *message, unsigned int messageLength,
MIDITimeStamp timestamp)
{
PmError err;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
assert(m);
/* printf("add %d to packet %lx len %d\n", message[0], m->packet, messageLength); */
m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
m->packet, timestamp, messageLength,
message);
if (m->packet == NULL) {
/* out of space, send the buffer and start refilling it */
/* make midi->packet non-null to fool midi_write_flush into sending */
m->packet = (MIDIPacket *) 4;
if ((err = midi_write_flush(midi, timestamp)) != pmNoError) return err;
m->packet = MIDIPacketListInit(m->packetList);
assert(m->packet); /* if this fails, it's a programming error */
m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
m->packet, timestamp, messageLength,
message);
assert(m->packet); /* can't run out of space on first message */
}
return pmNoError;
}
static PmError
midi_write_short(PmInternal *midi, PmEvent *event)
{
long when = event->timestamp;
long what = event->message;
MIDITimeStamp timestamp;
UInt64 when_ns;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
Byte message[4];
unsigned int messageLength;
if (m->packet == NULL) {
m->packet = MIDIPacketListInit(m->packetList);
/* this can never fail, right? failure would indicate something
unrecoverable */
assert(m->packet);
}
/* compute timestamp */
if (when == 0) when = midi->now;
/* if latency == 0, midi->now is not valid. We will just set it to zero */
if (midi->latency == 0) when = 0;
when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta;
/* make sure we don't go backward in time */
if (when_ns < m->last_time) when_ns = m->last_time;
m->last_time = when_ns;
timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);
message[0] = Pm_MessageStatus(what);
message[1] = Pm_MessageData1(what);
message[2] = Pm_MessageData2(what);
messageLength = midi_length(what);
/* Add this message to the packet list */
return send_packet(midi, message, messageLength, timestamp);
}
static PmError
midi_begin_sysex(PmInternal *midi, PmTimestamp when)
{
UInt64 when_ns;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
assert(m);
m->sysex_byte_count = 0;
/* compute timestamp */
if (when == 0) when = midi->now;
/* if latency == 0, midi->now is not valid. We will just set it to zero */
if (midi->latency == 0) when = 0;
when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta;
m->sysex_timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);
if (m->packet == NULL) {
m->packet = MIDIPacketListInit(m->packetList);
/* this can never fail, right? failure would indicate something
unrecoverable */
assert(m->packet);
}
return pmNoError;
}
static PmError
midi_end_sysex(PmInternal *midi, PmTimestamp when)
{
PmError err;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
assert(m);
/* make sure we don't go backward in time */
if (m->sysex_timestamp < m->last_time) m->sysex_timestamp = m->last_time;
/* if flush has been called in the meantime, packet list is NULL */
if (m->packet == NULL) {
m->packet = MIDIPacketListInit(m->packetList);
assert(m->packet);
}
/* now send what's in the buffer */
err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count,
m->sysex_timestamp);
m->sysex_byte_count = 0;
if (err != pmNoError) {
m->packet = NULL; /* flush everything in the packet list */
return err;
}
return pmNoError;
}
static PmError
midi_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp)
{
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
assert(m);
if (m->sysex_byte_count >= SYSEX_BUFFER_SIZE) {
PmError err = midi_end_sysex(midi, timestamp);
if (err != pmNoError) return err;
}
m->sysex_buffer[m->sysex_byte_count++] = byte;
return pmNoError;
}
static PmError
midi_write_realtime(PmInternal *midi, PmEvent *event)
{
/* to send a realtime message during a sysex message, first
flush all pending sysex bytes into packet list */
PmError err = midi_end_sysex(midi, 0);
if (err != pmNoError) return err;
/* then we can just do a normal midi_write_short */
return midi_write_short(midi, event);
}
static unsigned int midi_has_host_error(PmInternal *midi)
{
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
return (m->callback_error[0] != 0) || (m->error[0] != 0);
}
static void midi_get_host_error(PmInternal *midi, char *msg, unsigned int len)
{
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
msg[0] = 0; /* initialize to empty string */
if (m) { /* make sure there is an open device to examine */
if (m->error[0]) {
strncpy(msg, m->error, len);
m->error[0] = 0; /* clear the error */
} else if (m->callback_error[0]) {
strncpy(msg, m->callback_error, len);
m->callback_error[0] = 0; /* clear the error */
}
msg[len - 1] = 0; /* make sure string is terminated */
}
}
MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp)
{
UInt64 nanos;
if (timestamp <= 0) {
return (MIDITimeStamp)0;
} else {
nanos = (UInt64)timestamp * (UInt64)1000000;
return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos);
}
}
PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp)
{
UInt64 nanos;
nanos = AudioConvertHostTimeToNanos(timestamp);
return (PmTimestamp)(nanos / (UInt64)1000000);
}
//
// Code taken from http://developer.apple.com/qa/qa2004/qa1374.html
//////////////////////////////////////
// Obtain the name of an endpoint without regard for whether it has connections.
// The result should be released by the caller.
CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
{
CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
CFStringRef str;
// begin with the endpoint's name
str = NULL;
MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str);
if (str != NULL) {
CFStringAppend(result, str);
CFRelease(str);
}
MIDIEntityRef entity = NULL;
MIDIEndpointGetEntity(endpoint, &entity);
if (entity == NULL)
// probably virtual
return result;
if (CFStringGetLength(result) == 0) {
// endpoint name has zero length -- try the entity
str = NULL;
MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str);
if (str != NULL) {
CFStringAppend(result, str);
CFRelease(str);
}
}
// now consider the device's name
MIDIDeviceRef device = NULL;
MIDIEntityGetDevice(entity, &device);
if (device == NULL)
return result;
str = NULL;
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str);
if (str != NULL) {
// if an external device has only one entity, throw away
// the endpoint name and just use the device name
if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) {
CFRelease(result);
return str;
} else {
// does the entity name already start with the device name?
// (some drivers do this though they shouldn't)
// if so, do not prepend
if (CFStringCompareWithOptions( result, /* endpoint name */
str /* device name */,
CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) {
// prepend the device name to the entity name
if (CFStringGetLength(result) > 0)
CFStringInsert(result, 0, CFSTR(" "));
CFStringInsert(result, 0, str);
}
CFRelease(str);
}
}
return result;
}
// Obtain the name of an endpoint, following connections.
// The result should be released by the caller.
static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
{
CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
CFStringRef str;
OSStatus err;
int i;
// Does the endpoint have connections?
CFDataRef connections = NULL;
int nConnected = 0;
bool anyStrings = false;
err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections);
if (connections != NULL) {
// It has connections, follow them
// Concatenate the names of all connected devices
nConnected = CFDataGetLength(connections) / sizeof(MIDIUniqueID);
if (nConnected) {
const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
for (i = 0; i < nConnected; ++i, ++pid) {
MIDIUniqueID id = EndianS32_BtoN(*pid);
MIDIObjectRef connObject;
MIDIObjectType connObjectType;
err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType);
if (err == noErr) {
if (connObjectType == kMIDIObjectType_ExternalSource ||
connObjectType == kMIDIObjectType_ExternalDestination) {
// Connected to an external device's endpoint (10.3 and later).
str = EndpointName((MIDIEndpointRef)(connObject), true);
} else {
// Connected to an external device (10.2) (or something else, catch-all)
str = NULL;
MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str);
}
if (str != NULL) {
if (anyStrings)
CFStringAppend(result, CFSTR(", "));
else anyStrings = true;
CFStringAppend(result, str);
CFRelease(str);
}
}
}
}
CFRelease(connections);
}
if (anyStrings)
return result;
// Here, either the endpoint had no connections, or we failed to obtain names for any of them.
return EndpointName(endpoint, false);
}
char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint)
{
#ifdef OLDCODE
MIDIEntityRef entity;
MIDIDeviceRef device;
CFStringRef endpointName = NULL;
CFStringRef deviceName = NULL;
#endif
CFStringRef fullName = NULL;
CFStringEncoding defaultEncoding;
char* newName;
/* get the default string encoding */
defaultEncoding = CFStringGetSystemEncoding();
fullName = ConnectedEndpointName(endpoint);
#ifdef OLDCODE
/* get the entity and device info */
MIDIEndpointGetEntity(endpoint, &entity);
MIDIEntityGetDevice(entity, &device);
/* create the nicely formated name */
MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName);
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName);
if (deviceName != NULL) {
fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"),
deviceName, endpointName);
} else {
fullName = endpointName;
}
#endif
/* copy the string into our buffer */
newName = (char *) malloc(CFStringGetLength(fullName) + 1);
CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1,
defaultEncoding);
/* clean up */
#ifdef OLDCODE
if (endpointName) CFRelease(endpointName);
if (deviceName) CFRelease(deviceName);
#endif
if (fullName) CFRelease(fullName);
return newName;
}
pm_fns_node pm_macosx_in_dictionary = {
none_write_short,
none_sysex,
none_sysex,
none_write_byte,
none_write_short,
none_write_flush,
none_synchronize,
midi_in_open,
midi_abort,
midi_in_close,
success_poll,
midi_has_host_error,
midi_get_host_error,
};
pm_fns_node pm_macosx_out_dictionary = {
midi_write_short,
midi_begin_sysex,
midi_end_sysex,
midi_write_byte,
midi_write_realtime,
midi_write_flush,
midi_synchronize,
midi_out_open,
midi_abort,
midi_out_close,
success_poll,
midi_has_host_error,
midi_get_host_error,
};
PmError pm_macosxcm_init(void)
{
ItemCount numInputs, numOutputs, numDevices;
MIDIEndpointRef endpoint;
int i;
OSStatus macHostError;
char *error_text;
/* Determine the number of MIDI devices on the system */
numDevices = MIDIGetNumberOfDevices();
numInputs = MIDIGetNumberOfSources();
numOutputs = MIDIGetNumberOfDestinations();
/* Return prematurely if no devices exist on the system
Note that this is not an error. There may be no devices.
Pm_CountDevices() will return zero, which is correct and
useful information
*/
if (numDevices <= 0) {
return pmNoError;
}
/* Initialize the client handle */
macHostError = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client);
if (macHostError != noErr) {
error_text = "MIDIClientCreate() in pm_macosxcm_init()";
goto error_return;
}
/* Create the input port */
macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), readProc,
NULL, &portIn);
if (macHostError != noErr) {
error_text = "MIDIInputPortCreate() in pm_macosxcm_init()";
goto error_return;
}
/* Create the output port */
macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut);
if (macHostError != noErr) {
error_text = "MIDIOutputPortCreate() in pm_macosxcm_init()";
goto error_return;
}
/* Iterate over the MIDI input devices */
for (i = 0; i < numInputs; i++) {
endpoint = MIDIGetSource(i);
if (endpoint == NULL) {
continue;
}
/* set the first input we see to the default */
if (pm_default_input_device_id == -1)
pm_default_input_device_id = pm_descriptor_index;
/* Register this device with PortMidi */
pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
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;
}
/* set the first output we see to the default */
if (pm_default_output_device_id == -1)
pm_default_output_device_id = pm_descriptor_index;
/* Register this device with PortMidi */
pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
FALSE, (void*)endpoint, &pm_macosx_out_dictionary);
}
return pmNoError;
error_return:
pm_hosterror = macHostError;
sprintf(pm_hosterror_text, "Host error %ld: %s\n", macHostError, error_text);
pm_macosxcm_term(); /* clear out any opened ports */
return pmHostError;
}
void pm_macosxcm_term(void)
{
if (client != NULL) MIDIClientDispose(client);
if (portIn != NULL) MIDIPortDispose(portIn);
if (portOut != NULL) MIDIPortDispose(portOut);
}