diff options
Diffstat (limited to 'pd/portaudio/portmidi-macosx')
-rw-r--r-- | pd/portaudio/portmidi-macosx/Makefile | 24 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/README | 12 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pmdarwin.c | 36 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pminternal.h | 100 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pmmacosx.c | 336 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pmmacosx.h | 4 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pmtest | bin | 0 -> 24685 bytes | |||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pmtest.c | 136 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pmutil.c | 86 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/pmutil.h | 44 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/portmidi.c | 358 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/portmidi.h | 338 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/porttime.h | 30 | ||||
-rw-r--r-- | pd/portaudio/portmidi-macosx/ptdarwin.c | 58 |
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 Binary files differnew file mode 100644 index 00000000..8adc5334 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmtest 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); +} + + + |