aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/portmidi-macosx
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portaudio/portmidi-macosx')
-rw-r--r--pd/portaudio/portmidi-macosx/Makefile24
-rw-r--r--pd/portaudio/portmidi-macosx/README12
-rw-r--r--pd/portaudio/portmidi-macosx/pmdarwin.c36
-rw-r--r--pd/portaudio/portmidi-macosx/pminternal.h100
-rw-r--r--pd/portaudio/portmidi-macosx/pmmacosx.c336
-rw-r--r--pd/portaudio/portmidi-macosx/pmmacosx.h4
-rw-r--r--pd/portaudio/portmidi-macosx/pmtestbin0 -> 24685 bytes
-rw-r--r--pd/portaudio/portmidi-macosx/pmtest.c136
-rw-r--r--pd/portaudio/portmidi-macosx/pmutil.c86
-rw-r--r--pd/portaudio/portmidi-macosx/pmutil.h44
-rw-r--r--pd/portaudio/portmidi-macosx/portmidi.c358
-rw-r--r--pd/portaudio/portmidi-macosx/portmidi.h338
-rw-r--r--pd/portaudio/portmidi-macosx/porttime.h30
-rw-r--r--pd/portaudio/portmidi-macosx/ptdarwin.c58
14 files changed, 1562 insertions, 0 deletions
diff --git a/pd/portaudio/portmidi-macosx/Makefile b/pd/portaudio/portmidi-macosx/Makefile
new file mode 100644
index 00000000..d8667355
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/Makefile
@@ -0,0 +1,24 @@
+CC = cc
+CFLAGS = -Wmost
+LDFLAGS = -framework Carbon -framework CoreMIDI
+OBJS = ptdarwin.o pmutil.o pmmacosx.o pmdarwin.o portmidi.o
+LIBS =
+
+all: libportmidi.a pmtest
+
+libportmidi.a: portmidi.h porttime.h pminternal.h $(OBJS)
+ rm -f libportmidi.a
+ ar rv libportmidi.a $(OBJS)
+ ranlib libportmidi.a
+
+pmtest: pmtest.c libportmidi.a
+ $(CC) $(CFLAGS) pmtest.c $(OBJS) -o pmtest $(LDFLAGS) $(LIBS)
+
+pmmacosx.o: pmmacosx.c portmidi.h pminternal.h pmmacosx.h porttime.h
+pmdarwin.o: pmdarwin.c portmidi.h pmmacosx.h
+pmutil.o: pmutil.c portmidi.h pmutil.h pminternal.h
+portmidi.o: portmidi.c portmidi.h pminternal.h
+ptdarwin.o: ptdarwin.c porttime.h portmidi.h
+
+clean:
+ rm -f pmtest *.o
diff --git a/pd/portaudio/portmidi-macosx/README b/pd/portaudio/portmidi-macosx/README
new file mode 100644
index 00000000..89c0e6fa
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/README
@@ -0,0 +1,12 @@
+PortMidi for MacOS X / Darwin
+Jon Parise <jparise@cmu.edu>
+$Date: 2002-07-29 17:06:16 $
+
+This is the MacOS X / Darwin port of the PortMidi library from the Carnegie
+Mellon Computer Music Group. It is based on the Apple CoreAudio MIDI
+interface.
+
+This port was finished in early 2002. At this point, I consider the code
+base complete.
+
+- Jon
diff --git a/pd/portaudio/portmidi-macosx/pmdarwin.c b/pd/portaudio/portmidi-macosx/pmdarwin.c
new file mode 100644
index 00000000..510339c3
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmdarwin.c
@@ -0,0 +1,36 @@
+/*
+ * PortMidi OS-dependent interface for Darwin (MacOS X)
+ * Jon Parise <jparise@cmu.edu>
+ *
+ * $Id: pmdarwin.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $
+ */
+
+/*
+ * This file only needs to implement pm_init(), which calls various
+ * routines to register the available midi devices. This file must
+ * be separate from the main portmidi.c file because it is system
+ * dependent, and it is separate from, say, pmwinmm.c, because it
+ * might need to register devices for winmm, directx, and others.
+ */
+
+#include <stdlib.h>
+#include "portmidi.h"
+#include "pmmacosx.h"
+
+PmError pm_init()
+{
+ return pm_macosx_init();
+}
+
+PmError pm_term()
+{
+ return pm_macosx_term();
+}
+
+PmDeviceID Pm_GetDefaultInputDeviceID() { return 0; };
+PmDeviceID Pm_GetDefaultOutputDeviceID() { return 0; };
+
+void *pm_alloc(size_t s) { return malloc(s); }
+
+void pm_free(void *ptr) { free(ptr); }
+
diff --git a/pd/portaudio/portmidi-macosx/pminternal.h b/pd/portaudio/portmidi-macosx/pminternal.h
new file mode 100644
index 00000000..2a92e16d
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pminternal.h
@@ -0,0 +1,100 @@
+/* pminternal.h -- header for interface implementations */
+
+/* this file is included by files that implement library internals */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/* these are defined in system-specific file */
+void *pm_alloc(size_t s);
+void pm_free(void *ptr);
+
+struct pm_internal_struct;
+
+/* these do not use PmInternal because it is not defined yet... */
+typedef PmError (*pm_write_fn)(struct pm_internal_struct *midi,
+ PmEvent *buffer, long length);
+typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi,
+ void *driverInfo);
+typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi);
+typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi);
+
+typedef struct {
+ pm_write_fn write;
+ pm_open_fn open;
+ pm_abort_fn abort;
+ pm_close_fn close;
+} pm_fns_node, *pm_fns_type;
+
+/* when open fails, the dictionary gets this set of functions: */
+extern pm_fns_node pm_none_dictionary;
+
+typedef struct {
+ PmDeviceInfo pub;
+ void *descriptor; /* system-specific data to open device */
+ pm_fns_type dictionary;
+} descriptor_node, *descriptor_type;
+
+
+#define pm_descriptor_max 32
+extern descriptor_node descriptors[pm_descriptor_max];
+extern int descriptor_index;
+
+
+typedef unsigned long (*time_get_proc_type)(void *time_info);
+
+typedef struct pm_internal_struct {
+ short write_flag; /* MIDI_IN, or MIDI_OUT */
+ int device_id; /* which device is open (index to descriptors) */
+ PmTimeProcPtr time_proc; /* where to get the time */
+ void *time_info; /* pass this to get_time() */
+ PmEvent *buffer; /* input or output buffer */
+ long buffer_len; /* how big is the buffer */
+ long latency; /* time delay in ms between timestamps and actual output */
+ /* set to zero to get immediate, simple blocking output */
+ /* if latency is zero, timestamps will be ignored */
+ int overflow; /* set to non-zero if input is dropped */
+ int flush; /* flag to drop incoming sysex data because of overflow */
+ int sysex_in_progress; /* use for overflow management */
+ struct pm_internal_struct *thru;
+ PmTimestamp last_msg_time; /* timestamp of last message */
+ long head;
+ long tail;
+ pm_fns_type dictionary; /* implementation functions */
+ void *descriptor; /* system-dependent state */
+} PmInternal;
+
+
+typedef struct {
+ long head;
+ long tail;
+ long len;
+ long msg_size;
+ long overflow;
+ char *buffer;
+} PmQueueRep;
+
+
+PmError pm_init(void); /* defined in a system-specific file */
+PmError pm_term(void); /* defined in a system-specific file */
+int pm_in_device(int n, char *interf, char *device);
+int pm_out_device(int n, char *interf, char *device);
+PmError none_write(PmInternal *midi, PmEvent *buffer, long length);
+PmError pm_success_fn(PmInternal *midi);
+PmError pm_fail_fn(PmInternal *midi);
+long pm_in_poll(PmInternal *midi);
+long pm_out_poll(PmInternal *midi);
+
+PmError pm_add_device(char *interf, char *name, int input, void *descriptor,
+ pm_fns_type dictionary);
+
+void pm_enqueue(PmInternal *midi, PmEvent *event);
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/pd/portaudio/portmidi-macosx/pmmacosx.c b/pd/portaudio/portmidi-macosx/pmmacosx.c
new file mode 100644
index 00000000..0aafcf7f
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmmacosx.c
@@ -0,0 +1,336 @@
+/*
+ * Platform interface to the MacOS X CoreMIDI framework
+ *
+ * Jon Parise <jparise@cmu.edu>
+ *
+ * $Id: pmmacosx.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $
+ */
+
+#include "portmidi.h"
+#include "pminternal.h"
+#include "porttime.h"
+#include "pmmacosx.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <CoreServices/CoreServices.h>
+#include <CoreMIDI/MIDIServices.h>
+
+#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;
+}
diff --git a/pd/portaudio/portmidi-macosx/pmmacosx.h b/pd/portaudio/portmidi-macosx/pmmacosx.h
new file mode 100644
index 00000000..15e9551d
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmmacosx.h
@@ -0,0 +1,4 @@
+/* system-specific definitions */
+
+PmError pm_macosx_init(void);
+PmError pm_macosx_term(void);
diff --git a/pd/portaudio/portmidi-macosx/pmtest b/pd/portaudio/portmidi-macosx/pmtest
new file mode 100644
index 00000000..8adc5334
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmtest
Binary files differ
diff --git a/pd/portaudio/portmidi-macosx/pmtest.c b/pd/portaudio/portmidi-macosx/pmtest.c
new file mode 100644
index 00000000..5628d25e
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmtest.c
@@ -0,0 +1,136 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "portmidi.h"
+#include "porttime.h"
+#include "pminternal.h"
+
+#define LATENCY 0
+#define NUM_ECHOES 10
+
+int
+main()
+{
+ int i = 0;
+ int n = 0;
+ PmStream *midi_in;
+ PmStream *midi_out;
+ PmError err;
+ char line[80];
+ PmEvent buffer[NUM_ECHOES];
+ int transpose;
+ int delay;
+ int status, data1, data2;
+ int statusprefix;
+
+
+
+ /* always start the timer before you start midi */
+ Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
+
+
+ for (i = 0; i < Pm_CountDevices(); i++) {
+ const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
+ printf("%d: %s, %s", i, info->interf, info->name);
+ if (info->input) printf(" (input)");
+ if (info->output) printf(" (output)");
+ printf("\n");
+ }
+
+ /* OPEN INPUT DEVICE */
+
+ printf("Type input number: ");
+ while (n != 1) {
+ n = scanf("%d", &i);
+ gets(line);
+ }
+
+ err = Pm_OpenInput(&midi_in, i, NULL, 100, NULL, NULL, NULL);
+ if (err) {
+ printf("could not open midi device: %s\n", Pm_GetErrorText(err));
+ exit(1);
+ }
+ printf("Midi Input opened.\n");
+
+ /* OPEN OUTPUT DEVICE */
+
+ printf("Type output number: ");
+ n = 0;
+ while (n != 1) {
+ n = scanf("%d", &i);
+ gets(line);
+ }
+
+ err = Pm_OpenOutput(&midi_out, i, NULL, 0, NULL, NULL, LATENCY);
+ if (err) {
+ printf("could not open midi device: %s\n", Pm_GetErrorText(err));
+ exit(1);
+ }
+ printf("Midi Output opened with %d ms latency.\n", LATENCY);
+
+
+
+ /* Get input from user for parameters */
+ printf("Type number of milliseconds for echoes: ");
+ n = 0;
+ while (n != 1) {
+ n = scanf("%d", &delay);
+ gets(line);
+ }
+
+ printf("Type number of semitones to transpose up: ");
+ n = 0;
+ while (n != 1) {
+ n = scanf("%d", &transpose);
+ gets(line);
+ }
+
+
+
+ /* loop, echoing input back transposed with multiple taps */
+
+ printf("Press C2 on the keyboard (2 octaves below middle C) to quit.\nWaiting for MIDI input...\n");
+
+ do {
+ err = Pm_Read(midi_in, buffer, 1);
+ if (err == 0) continue; /* no bytes read. */
+
+ /* print a hash mark for each event read. */
+ printf("#");
+ fflush(stdout);
+
+ status = Pm_MessageStatus(buffer[0].message);
+ data1 = Pm_MessageData1(buffer[0].message);
+ data2 = Pm_MessageData2(buffer[0].message);
+ statusprefix = status >> 4;
+
+ /* ignore messages other than key-down and key-up */
+ if ((statusprefix != 0x9) && (statusprefix != 0x8)) continue;
+
+ printf("\nReceived key message = %X %X %X, at time %ld\n", status, data1, data2, buffer[0].timestamp);
+ fflush(stdout);
+
+ /* immediately send the echoes to PortMIDI */
+ for (i = 1; i < NUM_ECHOES; i++) {
+ buffer[i].message = Pm_Message(status, data1 + transpose, data2 >> i);
+ buffer[i].timestamp = buffer[0].timestamp + (i * delay);
+ }
+ Pm_Write(midi_out, buffer, NUM_ECHOES);
+ } while (data1 != 36); /* quit when C2 is pressed */
+
+ printf("Key C2 pressed. Exiting...\n");
+ fflush(stdout);
+
+ /* Give the echoes time to finish before quitting. */
+ sleep(((NUM_ECHOES * delay) / 1000) + 1);
+
+ Pm_Close(midi_in);
+ Pm_Close(midi_out);
+
+ printf("Done.\n");
+ return 0;
+}
+
+
+
diff --git a/pd/portaudio/portmidi-macosx/pmutil.c b/pd/portaudio/portmidi-macosx/pmutil.c
new file mode 100644
index 00000000..f3582a42
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmutil.c
@@ -0,0 +1,86 @@
+/* pmutil.c -- some helpful utilities for building midi
+ applications that use PortMidi
+ */
+#include "stdlib.h"
+#include "memory.h"
+#include "portmidi.h"
+#include "pmutil.h"
+#include "pminternal.h"
+
+
+PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg)
+{
+ PmQueueRep *queue = (PmQueueRep *) malloc(sizeof(PmQueueRep));
+ if (!queue) return NULL;
+ queue->len = num_msgs * bytes_per_msg;
+ queue->buffer = malloc(queue->len);
+ if (!queue->buffer) {
+ free(queue);
+ return NULL;
+ }
+ queue->head = 0;
+ queue->tail = 0;
+ queue->msg_size = bytes_per_msg;
+ queue->overflow = FALSE;
+ return queue;
+}
+
+
+PmError Pm_QueueDestroy(PmQueue *q)
+{
+ PmQueueRep *queue = (PmQueueRep *) q;
+ if (!queue || !queue->buffer) return pmBadPtr;
+ free(queue->buffer);
+ free(queue);
+ return pmNoError;
+}
+
+
+PmError Pm_Dequeue(PmQueue *q, void *msg)
+{
+ long head;
+ PmQueueRep *queue = (PmQueueRep *) q;
+ if (queue->overflow) {
+ queue->overflow = FALSE;
+ return pmBufferOverflow;
+ }
+ head = queue->head; /* make sure this is written after access */
+ if (head == queue->tail) return 0;
+ memcpy(msg, queue->buffer + head, queue->msg_size);
+ head += queue->msg_size;
+ if (head == queue->len) head = 0;
+ queue->head = head;
+ return 1; /* success */
+}
+
+
+/* source should not enqueue data if overflow is set */
+/**/
+PmError Pm_Enqueue(PmQueue *q, void *msg)
+{
+ PmQueueRep *queue = (PmQueueRep *) q;
+ long tail = queue->tail;
+ memcpy(queue->buffer + tail, msg, queue->msg_size);
+ tail += queue->msg_size;
+ if (tail == queue->len) tail = 0;
+ if (tail == queue->head) {
+ queue->overflow = TRUE;
+ /* do not update tail, so message is lost */
+ return pmBufferOverflow;
+ }
+ queue->tail = tail;
+ return pmNoError;
+}
+
+
+int Pm_QueueFull(PmQueue *q)
+{
+ PmQueueRep *queue = (PmQueueRep *) q;
+ long tail = queue->tail;
+ tail += queue->msg_size;
+ if (tail == queue->len) {
+ tail = 0;
+ }
+ return (tail == queue->head);
+}
+
diff --git a/pd/portaudio/portmidi-macosx/pmutil.h b/pd/portaudio/portmidi-macosx/pmutil.h
new file mode 100644
index 00000000..b6268ed3
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/pmutil.h
@@ -0,0 +1,44 @@
+/* pmutil.h -- some helpful utilities for building midi
+ applications that use PortMidi
+ */
+
+typedef void PmQueue;
+
+/*
+ A single-reader, single-writer queue is created by
+ Pm_QueueCreate(), which takes the number of messages and
+ the message size as parameters. The queue only accepts
+ fixed sized messages. Returns NULL if memory cannot be allocated.
+
+ Pm_QueueDestroy() destroys the queue and frees its storage.
+ */
+
+PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg);
+PmError Pm_QueueDestroy(PmQueue *queue);
+
+/*
+ Pm_Dequeue() removes one item from the queue, copying it into msg.
+ Returns 1 if successful, and 0 if the queue is empty.
+ Returns pmBufferOverflow and clears the overflow flag if
+ the flag is set.
+ */
+PmError Pm_Dequeue(PmQueue *queue, void *msg);
+
+
+/*
+ Pm_Enqueue() inserts one item into the queue, copying it from msg.
+ Returns pmNoError if successful and pmBufferOverflow if the queue was
+ already full. If pmBufferOverflow is returned, the overflow flag is set.
+ */
+PmError Pm_Enqueue(PmQueue *queue, void *msg);
+
+
+/*
+ Pm_QueueFull() returns non-zero if the queue is full
+ Pm_QueueEmpty() returns non-zero if the queue is empty
+
+ Either condition may change immediately because a parallel
+ enqueue or dequeue operation could be in progress.
+ */
+int Pm_QueueFull(PmQueue *queue);
+#define Pm_QueueEmpty(m) (m->head == m->tail)
diff --git a/pd/portaudio/portmidi-macosx/portmidi.c b/pd/portaudio/portmidi-macosx/portmidi.c
new file mode 100644
index 00000000..c2a32ae7
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/portmidi.c
@@ -0,0 +1,358 @@
+#include "stdlib.h"
+#include "portmidi.h"
+#include "pminternal.h"
+
+#define is_empty(midi) ((midi)->tail == (midi)->head)
+
+static int pm_initialized = FALSE;
+
+int descriptor_index = 0;
+descriptor_node descriptors[pm_descriptor_max];
+
+
+/* pm_add_device -- describe interface/device pair to library
+ *
+ * This is called at intialization time, once for each
+ * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1)
+ * The strings are retained but NOT COPIED, so do not destroy them!
+ *
+ * returns pmInvalidDeviceId if device memory is exceeded
+ * otherwise returns pmNoError
+ */
+PmError pm_add_device(char *interf, char *name, int input,
+ void *descriptor, pm_fns_type dictionary)
+{
+ if (descriptor_index >= pm_descriptor_max) {
+ return pmInvalidDeviceId;
+ }
+ descriptors[descriptor_index].pub.interf = interf;
+ descriptors[descriptor_index].pub.name = name;
+ descriptors[descriptor_index].pub.input = input;
+ descriptors[descriptor_index].pub.output = !input;
+ descriptors[descriptor_index].descriptor = descriptor;
+ descriptors[descriptor_index].dictionary = dictionary;
+ descriptor_index++;
+ return pmNoError;
+}
+
+
+PmError Pm_Initialize( void )
+{
+ if (!pm_initialized) {
+ PmError err = pm_init(); /* defined by implementation specific file */
+ if (err) return err;
+ pm_initialized = TRUE;
+ }
+ return pmNoError;
+}
+
+
+PmError Pm_Terminate( void )
+{
+ PmError err = pmNoError;
+ if (pm_initialized) {
+ err = pm_term(); /* defined by implementation specific file */
+ /* note that even when pm_term() fails, we mark portmidi as
+ not initialized */
+ pm_initialized = FALSE;
+ }
+ return err;
+}
+
+
+int Pm_CountDevices( void )
+{
+ PmError err = Pm_Initialize();
+ if (err) return err;
+
+ return descriptor_index;
+}
+
+
+const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id )
+{
+ PmError err = Pm_Initialize();
+ if (err) return NULL;
+
+ if (id >= 0 && id < descriptor_index) {
+ return &descriptors[id].pub;
+ }
+ return NULL;
+}
+
+
+/* failure_fn -- "noop" function pointer */
+/**/
+PmError failure_fn(PmInternal *midi)
+{
+ return pmBadPtr;
+}
+
+
+/* pm_success_fn -- "noop" function pointer */
+/**/
+PmError pm_success_fn(PmInternal *midi)
+{
+ return pmNoError;
+}
+
+
+PmError none_write(PmInternal *midi, PmEvent *buffer, long length)
+{
+ return length; /* if we return 0, caller might get into a loop */
+}
+
+PmError pm_fail_fn(PmInternal *midi)
+{
+ return pmBadPtr;
+}
+
+static PmError none_open(PmInternal *midi, void *driverInfo)
+{
+ return pmBadPtr;
+}
+
+#define none_abort pm_fail_fn
+
+#define none_close pm_fail_fn
+
+
+pm_fns_node pm_none_dictionary = {
+ none_write, none_open,
+ none_abort, none_close };
+
+
+/* Pm_Read -- read up to length longs from source into buffer */
+/*
+ * returns number of longs actually read, or error code
+ When the reader wants data:
+ if overflow_flag:
+ do not get anything
+ empty the buffer (read_ptr = write_ptr)
+ clear overflow_flag
+ return pmBufferOverflow
+ get data
+ return number of messages
+
+
+ */
+PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length)
+{
+ PmInternal *midi = (PmInternal *) stream;
+ int n = 0;
+ long head = midi->head;
+ while (head != midi->tail && n < length) {
+ *buffer++ = midi->buffer[head++];
+ if (head == midi->buffer_len) head = 0;
+ n++;
+ }
+ midi->head = head;
+ if (midi->overflow) {
+ midi->head = midi->tail;
+ midi->overflow = FALSE;
+ return pmBufferOverflow;
+ }
+ return n;
+}
+
+
+PmError Pm_Poll( PortMidiStream *stream )
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return midi->head != midi->tail;
+}
+
+
+PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length)
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return (*midi->dictionary->write)(midi, buffer, length);
+}
+
+
+PmError Pm_WriteShort( PortMidiStream *stream, long when, long msg)
+{
+ PmEvent event;
+ event.timestamp = when;
+ event.message = msg;
+ return Pm_Write(stream, &event, 1);
+}
+
+
+PmError Pm_OpenInput( PortMidiStream** stream,
+ PmDeviceID inputDevice,
+ void *inputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ PmStream *thru)
+{
+ PmInternal *midi;
+
+ PmError err = Pm_Initialize();
+ if (err) return err;
+
+ if (inputDevice < 0 || inputDevice >= descriptor_index) {
+ return pmInvalidDeviceId;
+ }
+
+ if (!descriptors[inputDevice].pub.input) {
+ return pmInvalidDeviceId;
+ }
+
+ midi = (PmInternal *) malloc(sizeof(PmInternal));
+ *stream = midi;
+ if (!midi) return pmInsufficientMemory;
+
+ midi->head = 0;
+ midi->tail = 0;
+ midi->dictionary = &pm_none_dictionary;
+ midi->overflow = FALSE;
+ midi->flush = FALSE;
+ midi->sysex_in_progress = FALSE;
+ midi->buffer_len = bufferSize;
+ midi->buffer = (PmEvent *) pm_alloc(sizeof(PmEvent) * midi->buffer_len);
+ if (!midi->buffer) return pmInsufficientMemory;
+ midi->latency = 0;
+ midi->thru = thru;
+ midi->time_proc = time_proc;
+ midi->time_info = time_info;
+ midi->device_id = inputDevice;
+ midi->dictionary = descriptors[inputDevice].dictionary;
+ midi->write_flag = FALSE;
+ err = (*midi->dictionary->open)(midi, inputDriverInfo);
+ if (err) {
+ pm_free(midi->buffer);
+ *stream = NULL;
+ }
+ return err;
+}
+
+
+PmError Pm_OpenOutput( PortMidiStream** stream,
+ PmDeviceID outputDevice,
+ void *outputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ long latency )
+{
+ PmInternal *midi;
+
+ PmError err = Pm_Initialize();
+ if (err) return err;
+
+ if (outputDevice < 0 || outputDevice >= descriptor_index) {
+ return pmInvalidDeviceId;
+ }
+
+ if (!descriptors[outputDevice].pub.output) {
+ return pmInvalidDeviceId;
+ }
+
+ midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
+ *stream = midi;
+ if (!midi) return pmInsufficientMemory;
+
+ midi->head = 0;
+ midi->tail = 0;
+ midi->buffer_len = bufferSize;
+ midi->buffer = NULL;
+ midi->device_id = outputDevice;
+ midi->dictionary = descriptors[outputDevice].dictionary;
+ midi->time_proc = time_proc;
+ midi->time_info = time_info;
+ midi->latency = latency;
+ midi->write_flag = TRUE;
+ err = (*midi->dictionary->open)(midi, outputDriverInfo);
+ if (err) {
+ *stream = NULL;
+ pm_free(midi); // Fixed by Ning Hu, Sep.2001
+ }
+ return err;
+}
+
+
+PmError Pm_Abort( PortMidiStream* stream )
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return (*midi->dictionary->abort)(midi);
+}
+
+
+PmError Pm_Close( PortMidiStream *stream )
+{
+ PmInternal *midi = (PmInternal *) stream;
+ return (*midi->dictionary->close)(midi);
+}
+
+
+const char *Pm_GetErrorText( PmError errnum )
+{
+ const char *msg;
+
+ switch(errnum)
+ {
+ case pmNoError: msg = "Success"; break;
+ case pmHostError: msg = "Host error."; break;
+ case pmInvalidDeviceId: msg = "Invalid device ID."; break;
+ case pmInsufficientMemory: msg = "Insufficient memory."; break;
+ case pmBufferTooSmall: msg = "Buffer too small."; break;
+ case pmBadPtr: msg = "Bad pointer."; break;
+ case pmInternalError: msg = "Internal PortMidi Error."; break;
+ default: msg = "Illegal error number."; break;
+ }
+ return msg;
+}
+
+
+long pm_next_time(PmInternal *midi)
+{
+ return midi->buffer[midi->head].timestamp;
+}
+
+
+/* source should not enqueue data if overflow is set */
+/*
+ When producer has data to enqueue:
+ if buffer is full:
+ set overflow_flag and flush_flag
+ return
+ else if overflow_flag:
+ return
+ else if flush_flag:
+ if sysex message is in progress:
+ return
+ else:
+ clear flush_flag
+ // fall through to enqueue data
+ enqueue the data
+
+ */
+void pm_enqueue(PmInternal *midi, PmEvent *event)
+{
+ long tail = midi->tail;
+ midi->buffer[tail++] = *event;
+ if (tail == midi->buffer_len) tail = 0;
+ if (tail == midi->head || midi->overflow) {
+ midi->overflow = TRUE;
+ midi->flush = TRUE;
+ return;
+ }
+ if (midi->flush) {
+ if (midi->sysex_in_progress) return;
+ else midi->flush = FALSE;
+ }
+ midi->tail = tail;
+}
+
+
+int pm_queue_full(PmInternal *midi)
+{
+ long tail = midi->tail + 1;
+ if (tail == midi->buffer_len) tail = 0;
+ return tail == midi->head;
+}
+
+
+
diff --git a/pd/portaudio/portmidi-macosx/portmidi.h b/pd/portaudio/portmidi-macosx/portmidi.h
new file mode 100644
index 00000000..3e648c90
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/portmidi.h
@@ -0,0 +1,338 @@
+#ifndef PORT_MIDI_H
+#define PORT_MIDI_H
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * PortMidi Portable Real-Time Audio Library
+ * PortMidi API Header File
+ * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ * Copyright (c) 2001 Roger B. Dannenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* CHANGELOG FOR PORTMIDI -- THIS VERSION IS 1.0
+ *
+ * 21Jan02 RBD Added tests in Pm_OpenInput() and Pm_OpenOutput() to
+ * prevent opening an input as output and vice versa.
+ * Added comments and documentation.
+ * Implemented Pm_Terminate().
+ */
+
+#ifndef FALSE
+ #define FALSE 0
+#endif
+#ifndef TRUE
+ #define TRUE 1
+#endif
+
+
+typedef enum {
+ pmNoError = 0,
+
+ pmHostError = -10000,
+ pmInvalidDeviceId, /* out of range or
+ output device when input is requested or
+ input device when output is requested */
+ //pmInvalidFlag,
+ pmInsufficientMemory,
+ pmBufferTooSmall,
+ pmBufferOverflow,
+ pmBadPtr,
+ pmInternalError
+} PmError;
+
+/*
+ Pm_Initialize() is the library initialisation function - call this before
+ using the library.
+*/
+
+PmError Pm_Initialize( void );
+
+/*
+ Pm_Terminate() is the library termination function - call this after
+ using the library.
+*/
+
+PmError Pm_Terminate( void );
+
+/*
+ Return host specific error number. All host-specific errors are translated
+ to the single error class pmHostError. To find out the original error
+ number, call Pm_GetHostError().
+ This can be called after a function returns a PmError equal to pmHostError.
+*/
+int Pm_GetHostError();
+
+/*
+ Translate the error number into a human readable message.
+*/
+const char *Pm_GetErrorText( PmError errnum );
+
+
+/*
+ Device enumeration mechanism.
+
+ Device ids range from 0 to Pm_CountDevices()-1.
+
+ Devices may support input, output or both. Device 0 is always the "default"
+ device. Other platform specific devices are specified by positive device
+ ids.
+*/
+
+typedef int PmDeviceID;
+#define pmNoDevice -1
+
+typedef struct {
+ int structVersion;
+ const char *interf;
+ const char *name;
+ int input; /* true iff input is available */
+ int output; /* true iff output is available */
+} PmDeviceInfo;
+
+
+int Pm_CountDevices( void );
+/*
+ Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID()
+
+ Return the default device ID or pmNoDevice if there is no devices.
+ The result can be passed to Pm_OpenMidi().
+
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+
+ set PM_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ID by using
+ the supplied application "pm_devs".
+*/
+PmDeviceID Pm_GetDefaultInputDeviceID( void );
+PmDeviceID Pm_GetDefaultOutputDeviceID( void );
+
+/*
+ PmTimestamp is used to represent a millisecond clock with arbitrary
+ start time. The type is used for all MIDI timestampes and clocks.
+*/
+
+typedef long PmTimestamp;
+
+/* TRUE if t1 before t2? */
+#define PmBefore(t1,t2) ((t1-t2) < 0)
+
+
+/*
+ Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure
+ referring to the device specified by id.
+ If id is out of range the function returns NULL.
+
+ The returned structure is owned by the PortMidi implementation and must
+ not be manipulated or freed. The pointer is guaranteed to be valid
+ between calls to Pm_Initialize() and Pm_Terminate().
+*/
+
+const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id );
+
+
+/*
+ A single PortMidiStream is a descriptor for an open MIDI device.
+*/
+
+typedef void PortMidiStream;
+#define PmStream PortMidiStream
+
+typedef PmTimestamp (*PmTimeProcPtr)(void *time_info);
+
+
+/*
+ Pm_Open() opens a device; for either input or output.
+
+ Port is the address of a PortMidiStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ inputDevice is the id of the device used for input (see PmDeviceID above.)
+
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or handle processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+
+ outputDevice is the id of the device used for output (see PmDeviceID above.)
+
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or handle processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+
+ latency is the delay in milliseconds applied to timestamps to determine
+ when the output should actually occur.
+
+ time_proc is a pointer to a procedure that returns time in milliseconds. It
+ may be NULL, in which case a default millisecond timebase is used.
+
+ time_info is a pointer passed to time_proc.
+
+ thru points to a PmMidi descriptor opened for output; Midi input will be
+ copied to this output. To disable Midi thru, use NULL.
+
+ return value:
+ Upon success Pm_Open() returns PmNoError and places a pointer to a
+ valid PortMidiStream in the stream argument.
+ If a call to Pm_Open() fails a nonzero error code is returned (see
+ PMError above) and the value of port is invalid.
+
+*/
+
+PmError Pm_OpenInput( PortMidiStream** stream,
+ PmDeviceID inputDevice,
+ void *inputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ PmStream* thru );
+
+
+PmError Pm_OpenOutput( PortMidiStream** stream,
+ PmDeviceID outputDevice,
+ void *outputDriverInfo,
+ long bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ long latency );
+
+
+/*
+ Pm_Abort() terminates outgoing messages immediately
+ */
+PmError Pm_Abort( PortMidiStream* stream );
+
+/*
+ Pm_Close() closes a midi stream, flushing any pending buffers.
+*/
+
+PmError Pm_Close( PortMidiStream* stream );
+
+
+/*
+ Pm_Message() encodes a short Midi message into a long word. If data1
+ and/or data2 are not present, use zero. The port parameter is the
+ index of the Midi port if the device supports more than one.
+
+ Pm_MessagePort(), Pm_MessageStatus(), Pm_MessageData1(), and
+ Pm_MessageData2() extract fields from a long-encoded midi message.
+*/
+
+#define Pm_Message(status, data1, data2) \
+ ((((data2) << 16) & 0xFF0000) | \
+ (((data1) << 8) & 0xFF00) | \
+ ((status) & 0xFF))
+
+#define Pm_MessageStatus(msg) ((msg) & 0xFF)
+#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF)
+#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF)
+
+/* All midi data comes in the form of PmEvent structures. A sysex
+ message is encoded as a sequence of PmEvent structures, with each
+ structure carrying 4 bytes of the message, i.e. only the first
+ PmEvent carries the status byte.
+
+ When receiving sysex messages, the sysex message is terminated
+ by either an EOX status byte (anywhere in the 4 byte message) or
+ by a non-real-time status byte in the low order byte of message.
+ If you get a non-real-time status byte, it means the sysex message
+ was somehow truncated. It is permissible to interleave real-time
+ messages within sysex messages.
+ */
+
+typedef long PmMessage;
+
+typedef struct {
+ PmMessage message;
+ PmTimestamp timestamp;
+} PmEvent;
+
+
+/*
+ Pm_Read() retrieves midi data into a buffer, and returns the number
+ of events read. Result is a non-negative number unless an error occurs,
+ in which case a PmError value will be returned.
+
+ Buffer Overflow
+
+ The problem: if an input overflow occurs, data will be lost, ultimately
+ because there is no flow control all the way back to the data source.
+ When data is lost, the receiver should be notified and some sort of
+ graceful recovery should take place, e.g. you shouldn't resume receiving
+ in the middle of a long sysex message.
+
+ With a lock-free fifo, which is pretty much what we're stuck with to
+ enable portability to the Mac, it's tricky for the producer and consumer
+ to synchronously reset the buffer and resume normal operation.
+
+ Solution: the buffer managed by PortMidi will be flushed when an overflow
+ occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow)
+ and ordinary processing resumes as soon as a new message arrives. The
+ remainder of a partial sysex message is not considered to be a "new
+ message" and will be flushed as well.
+
+*/
+
+PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length );
+
+/*
+ Pm_Poll() tests whether input is available, returning TRUE, FALSE, or
+ an error value.
+*/
+
+PmError Pm_Poll( PortMidiStream *stream);
+
+/*
+ Pm_Write() writes midi data from a buffer. This may contain short
+ messages or sysex messages that are converted into a sequence of PmEvent
+ structures. Use Pm_WriteSysEx() to write a sysex message stored as a
+ contiguous array of bytes.
+*/
+
+PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length );
+
+/*
+ Pm_WriteShort() writes a timestamped non-system-exclusive midi message.
+*/
+
+PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg);
+
+/*
+ Pm_WriteSysEx() writes a timestamped system-exclusive midi message.
+*/
+PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, char *msg);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_MIDI_H */
diff --git a/pd/portaudio/portmidi-macosx/porttime.h b/pd/portaudio/portmidi-macosx/porttime.h
new file mode 100644
index 00000000..8592106d
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/porttime.h
@@ -0,0 +1,30 @@
+/* porttime.h -- portable interface to millisecond timer */
+
+/* Should there be a way to choose the source of time here? */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+ ptNoError = 0,
+ ptHostError = -10000,
+ ptAlreadyStarted,
+ ptAlreadyStopped
+} PtError;
+
+
+typedef long PtTimestamp;
+
+typedef int (PtCallback)( PtTimestamp timestamp, void *userData );
+
+
+PtError Pt_Start(int resolution, PtCallback *callback, void *userData);
+PtError Pt_Stop();
+int Pt_Started();
+PtTimestamp Pt_Time();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/pd/portaudio/portmidi-macosx/ptdarwin.c b/pd/portaudio/portmidi-macosx/ptdarwin.c
new file mode 100644
index 00000000..51cf5fde
--- /dev/null
+++ b/pd/portaudio/portmidi-macosx/ptdarwin.c
@@ -0,0 +1,58 @@
+/*
+ * Portable timer implementation for Darwin / MacOS X
+ *
+ * Jon Parise <jparise@cmu.edu>
+ *
+ * $Id: ptdarwin.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include "porttime.h"
+
+#define TRUE 1
+#define FALSE 0
+
+static int time_started_flag = FALSE;
+static struct timeval time_offset;
+
+PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
+{
+ struct timezone tz;
+
+ if (callback) printf("error in porttime: callbacks not implemented\n");
+ time_started_flag = TRUE;
+ gettimeofday(&time_offset, &tz);
+
+ return ptNoError;
+}
+
+
+PtError Pt_Stop()
+{
+ time_started_flag = FALSE;
+ return ptNoError;
+}
+
+
+int Pt_Started()
+{
+ return time_started_flag;
+}
+
+
+PtTimestamp Pt_Time()
+{
+ long seconds, milliseconds;
+ struct timeval now;
+ struct timezone tz;
+
+ gettimeofday(&now, &tz);
+ seconds = now.tv_sec - time_offset.tv_sec;
+ milliseconds = (now.tv_usec - time_offset.tv_usec) / 1000;
+
+ return (seconds * 1000 + milliseconds);
+}
+
+
+