This is next-3-0-midi.c in view mode; [Download] [Up]
/* interface to NeXT 3.0 midi driver. * if using Franz, compile by: cc -DEXCL next-3-0-midi.c -c -O -o next-midi.o * * This version queues up bytes locally to avoid blocking. */ #ifndef EXCL #ifndef AKCL #error "missing compilation flag: need AKCL or EXCL !" #endif #endif #import <mach.h> #import <stdio.h> #import <stdlib.h> #import <mach_error.h> #import <signal.h> #import <servers/netname.h> #import <libc.h> #import <appkit/nextstd.h> #import <mididriver/midi_driver.h> #import <mididriver/midi_spec.h> #import <cthreads.h> static int foo; /* For debugging */ static port_t driverPort; /* Port for driver on particular host. */ static port_t ownerPort; /* Port that represents ownership */ static port_t dataPort; /* Port for incoming data. */ static port_t queuePort = PORT_NULL; /* Port for output queue notification messages */ static port_t exceptionPort; /* Port for timing exceptions */ static port_t alarmPort; /* To get periodic messages. */ static int maxQueueSize; /* Maximum output queue size */ static int halfQueueSize; /* Maximum output queue size/2 */ static int unit; /* Serial port to send to */ static port_set_name_t ports; /* Port set to listen for messages from driver */ static int quantaSize=1000; static int quantaTime=0; /* Thread Globals */ int Listening = 0; int Writing = 0; mutex_t queueLock = NULL; mutex_t listeningLock; mutex_t schedulerLock; /* Handler Declarations */ static void myDataReply(port_t replyPort, short unit, MIDIRawEvent *eventbuf, unsigned int count); static void myAlarmReply(port_t replyPort, int requestedTime, int actualTime); static void myExceptionReply(port_t replyPort, int exception); static void myQueueReply(port_t replyPort, short unit); /* MIDI Messages */ typedef struct _MIDIMessage { unsigned long time; unsigned long message; struct _MIDIMessage *next; } MIDIMessage; static MIDIMessage *firstMessage = NULL; static MIDIMessage *lastMessage = NULL; static int lastTime = -1; static int Scheduling; #define SCHEDULER_ADVANCE 50 void startScheduler() { Scheduling = 1; } void stopScheduler() { mutex_lock(schedulerLock); Scheduling = 0; mutex_unlock(schedulerLock); firstMessage = NULL; lastMessage = NULL; } /* forward decls */ static int manageOverflowQueue(void); int allNotesOff(void); int midiopen(int port) /* port=0 for A, 1 for B */ { kern_return_t r; static int firstTime = 1; int synchUnit; /* Serial port to listen for time code */ if (port==0) unit=MIDI_PORT_A_UNIT; else if (port==1) unit=MIDI_PORT_B_UNIT; else return KERN_FAILURE; r = netname_look_up(name_server_port, "","mididriver", &driverPort); if (r != KERN_SUCCESS) return r; r = port_allocate(task_self(), &ownerPort); if (r != KERN_SUCCESS) return 0; r = MIDIBecomeOwner(driverPort,ownerPort); if (r != KERN_SUCCESS) return r; r = MIDIClaimUnit(driverPort, ownerPort,unit); if (r != KERN_SUCCESS) return r; r = MIDISetClockMode(driverPort, ownerPort, synchUnit, MIDI_CLOCK_MODE_INTERNAL); if (r != KERN_SUCCESS) return r; r = MIDISetClockQuantum(driverPort, ownerPort, quantaSize); if (r != KERN_SUCCESS) return r; r = MIDISetSystemIgnores(driverPort, ownerPort, unit, MIDI_IGNORE_REAL_TIME); if (r != KERN_SUCCESS) return r; /* create the various ports */ if (queuePort == PORT_NULL) { r = port_allocate(task_self(), &queuePort); if (r != KERN_SUCCESS) return r; } r = port_allocate(task_self(), &exceptionPort); if (r != KERN_SUCCESS) return r; r = port_allocate(task_self(), &dataPort); if (r != KERN_SUCCESS) return r; r = port_allocate(task_self(), &alarmPort); if (r != KERN_SUCCESS) return r; /* create the port set and add the ports */ r = MIDIRequestExceptions(driverPort, ownerPort, exceptionPort); if (r != KERN_SUCCESS) return r; r = MIDIGetAvailableQueueSize(driverPort, ownerPort, unit, &maxQueueSize); if (r != KERN_SUCCESS) return r; halfQueueSize = maxQueueSize / 2; r = MIDISetClockTime(driverPort, ownerPort, quantaTime); if (r != KERN_SUCCESS) return r; r = MIDIStartClock(driverPort, ownerPort); if (r != KERN_SUCCESS) return r; /* DAJ: Moved this to here. Note that you only have to do it once, not * every invocation of midireadmessages(). */ r = MIDIRequestData(driverPort, ownerPort, unit, dataPort); if (r != KERN_SUCCESS) return r; if (!queueLock) { listeningLock = mutex_alloc(); schedulerLock = mutex_alloc(); queueLock = mutex_alloc(); } Listening = 0; if (firstTime) cthread_detach(cthread_fork((cthread_fn_t)manageOverflowQueue, (any_t)0)); firstTime = 0; /* startScheduler(); */ return KERN_SUCCESS; } int midisettime(int time); /* Forward decl */ int midiclose() { kern_return_t r; if (!driverPort || !ownerPort) return KERN_SUCCESS; /* r=allNotesOff(); */ midisettime(0); /* Frees up all structure that hasn't gone out. */ r = MIDIReleaseOwnership(driverPort,ownerPort); if (r != KERN_SUCCESS) return r; r = port_deallocate(task_self(), exceptionPort); if (r != KERN_SUCCESS) return r; r = port_deallocate(task_self(), dataPort); if (r != KERN_SUCCESS) return r; r = port_deallocate(task_self(), alarmPort); if (r != KERN_SUCCESS) return r; quantaTime=0; quantaSize=1000; if (r != KERN_SUCCESS) return r; return KERN_SUCCESS; } int midistarttimer() { return MIDIStartClock(driverPort, ownerPort); } int midistoptimer() { return MIDIStopClock(driverPort, ownerPort); } int midisetquantasize(int usec) { quantaSize=usec; return MIDISetClockQuantum(driverPort, ownerPort, usec); } typedef struct _queuedBytes {MIDIRawEvent theEvent; struct _queuedBytes *next; } queuedBytes, *queuedBytesPtr; static volatile queuedBytesPtr head = NULL; static volatile queuedBytesPtr tail = NULL; int midisettime(int time) { queuedBytes *tmp; quantaTime=time; mutex_lock(queueLock); while (head) { tmp = head; head = head->next; free(tmp); } tail = NULL; mutex_unlock(queueLock); MIDIClearQueue(driverPort,ownerPort,unit); return MIDISetClockTime(driverPort, ownerPort, time); } int midigettime(int *time) { kern_return_t r; r = MIDIGetClockTime(driverPort, ownerPort, &quantaTime); if (r != KERN_SUCCESS) return r; time[0]=quantaTime; time[1]=quantaSize; return KERN_SUCCESS; } static int pushBytesOnQueue(MIDIRawEvent *events,int size) { int i; mutex_lock(queueLock); for (i = 0; i<size; i++) { if (!head) { tail = head = malloc(sizeof(queuedBytes)); tail->theEvent = *events++; tail->next = NULL; } else { tail->next = malloc(sizeof(queuedBytes)); tail = tail->next; tail->theEvent = *events++; tail->next = NULL; } } mutex_unlock(queueLock); return 0; } static void myQueueReply(port_t replyPort,short unit) /* This gets invoked when the queue has enough room for more data. */ { kern_return_t r; int i,thisCount,count; MIDIRawEvent events[MIDI_MAX_EVENT]; MIDIRawEvent *p; queuedBytes *tmp; int shouldReenqueue; /* Warning: This depends on the fact that halfQueueSize is greater than * MIDI_MAX_EVENT. If NeXT ever changes that, this code will fail. */ mutex_lock(queueLock); for (count = 0; (head != NULL) && (count + MIDI_MAX_EVENT < halfQueueSize); count += thisCount) { p = events; for (i = 0; i<MIDI_MAX_EVENT && head; i++) { *p++ = head->theEvent; tmp = head; head = head->next; free(tmp); } if (!head) tail = NULL; mutex_unlock(queueLock); /* Watching out for deadlock */ thisCount = p-events; if (thisCount) MIDISendData(driverPort, ownerPort, unit, events, p-events); mutex_lock(queueLock); } shouldReenqueue = (head != NULL); mutex_unlock(queueLock); if (shouldReenqueue) MIDIRequestQueueNotification(driverPort, ownerPort, unit, queuePort, halfQueueSize); } int midiwritemessage(int message, int qtime) { kern_return_t r = KERN_SUCCESS; MIDIRawEvent events[16]; int byte=16; int size=(message & 0x03000000) >> 24; int i; message=message & 0xffffff; for (i=0; i<size; i++) { events[i].time=qtime; events[i].byte=(char)((message >> byte) & 0xff); byte -= 8; } if (head) /* No room in driver */ return pushBytesOnQueue(events,size); r = MIDISendData(driverPort, ownerPort, unit, events, size); if (r == MIDI_ERROR_QUEUE_FULL) { /* Request notification when at least half the queue is * available */ pushBytesOnQueue(events,size); r = MIDIRequestQueueNotification(driverPort, ownerPort, unit, queuePort, halfQueueSize); } return r; } int midireadmessages() { kern_return_t r; char msgBuf[MIDI_MAX_MSG_SIZE]; msg_header_t *msg = (msg_header_t *)msgBuf; MIDIReplyFunctions funcs = {0, 0, 0, 0}; int keepGoing = TRUE; funcs.dataReply = (MIDIDataReplyFunction)myDataReply; while (keepGoing) { msg->msg_local_port = dataPort; msg->msg_size = MIDI_MAX_MSG_SIZE; r = msg_receive(msg, (RCV_TIMEOUT | RCV_INTERRUPT), 0); /* Changed to 0 by daj */ switch (r) { case RCV_SUCCESS: r = MIDIHandleReply(msg, &funcs); /* Simplifed by DAJ */ break; case RCV_TIMED_OUT: r = KERN_SUCCESS; default: keepGoing = FALSE; break; } } return r; } static int manageOverflowQueue(void) { kern_return_t r; char msgBuf[MIDI_MAX_MSG_SIZE]; msg_header_t *msg = (msg_header_t *)msgBuf; MIDIReplyFunctions funcs = {0, 0, 0, 0}; funcs.queueReply = (MIDIQueueReplyFunction)myQueueReply; for (;;) { MIDIAwaitReply(queuePort,&funcs,MIDI_NO_TIMEOUT); } return r; } int midiflushrecv() { return KERN_SUCCESS; } int midiflushxmit() { return KERN_SUCCESS; } #ifdef EXCL #define MIDI_INPUT_HOOK 0 #endif #define DataTag 0x0 #define ChannelTag 0x4000000 #define SystemTag 0x8000000 #define MetaTag 0xc000000 static void myDataReply(port_t replyPort, short unit, MIDIRawEvent *eventbuf, unsigned int count) { static unsigned char status = 0; static int length = 0; static int datastart = 0; static int bytenum = 0; static int datalsh = 0; static unsigned typeid = 0; static unsigned message = 0; MIDIRawEvent *e; char data; #ifdef EXCL long lisp_call(int index, unsigned msg, unsigned tim); #endif #ifdef AKCL void MIDI_INPUT_HOOK(int msg, int tim); #endif for (e = eventbuf; count--; e++) { data = e->byte; if (data & MIDI_STATUSBIT) { status = data; typeid = (MIDI_TYPE_SYSTEM(status)) ? SystemTag : ChannelTag; if (MIDI_TYPE_1BYTE(status)) { length = 1; datastart = 0; } else if (MIDI_TYPE_2BYTE(status)) { length = 2; datastart = 0; } else { length = 3; datastart = 8; } datalsh = datastart; bytenum = 1; message = 0; } else { message |= ((data << datalsh) & (0xff << datalsh)); if (datalsh == 0) datalsh = datastart; else datalsh -= 8; bytenum++; } if (bytenum == length) { message |= (typeid | ((length << 24) & 0x03000000) | ((status << 16) & 0xff0000)); #ifdef EXCL lisp_call(MIDI_INPUT_HOOK, message, (unsigned)e->time); #endif #ifdef AKCL MIDI_INPUT_HOOK((int)message,(int)e->time); #endif bytenum = 1; message = 0; datalsh = datastart; } } } static void myAlarmReply(port_t replyPort, int requestedTime, int actualTime) { } static void myExceptionReply(port_t replyPort, int exception) { } int midiallnotesoff(int time) { int c,k,m; kern_return_t r; for (c=0;c<16;c++) for (k=0;k<128;k++) { /* cobble up note off message */ m=0x7800040 | ((c<<16) & 0xf0000) | ((k<<8) & 0xff00); r=midiwritemessage(m,time); if (r != KERN_SUCCESS) return r; } return KERN_SUCCESS; } int midihush (void) { #define NOTEOFF_ARRAY_SIZE (128*2) /* Use running status */ MIDIRawEvent arr[NOTEOFF_ARRAY_SIZE]; kern_return_t r; int chan = 0; int bytesToSend,i; MIDIRawEvent op; MIDIReplyFunctions funcs = {0}; for (i=0; i<128; i++) { /* Initialize array */ arr[i*2].byte = i; /* KeyNum */ arr[i*2+1].byte = 0; /* Velocity */ } /* Empty out ports of any old notification messages. */ while (MIDIAwaitReply(ports,&funcs,1) == KERN_SUCCESS) ; r = MIDIClearQueue(driverPort,ownerPort,unit); if (r != KERN_SUCCESS) return r; for (chan = 0; chan < 16; chan++) { op.byte = MIDI_NOTEOFF | chan; r = MIDISendData(driverPort,ownerPort,unit,&op,1); if (r != KERN_SUCCESS) return r; for (i = 0; i < NOTEOFF_ARRAY_SIZE; i += MIDI_MAX_EVENT) { bytesToSend = NOTEOFF_ARRAY_SIZE - i; if (bytesToSend > MIDI_MAX_EVENT) bytesToSend = MIDI_MAX_EVENT; r = MIDISendData(driverPort,ownerPort,unit,&arr[i],bytesToSend); if (r != KERN_SUCCESS) return r; } r = MIDIFlushQueue(driverPort,ownerPort,unit); if (r != KERN_SUCCESS) return r; r = MIDIRequestQueueNotification(driverPort, ownerPort,unit,queuePort, ((chan==15) ? maxQueueSize : NOTEOFF_ARRAY_SIZE+1)); if (r != KERN_SUCCESS) return r; r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT); if (r != KERN_SUCCESS) return r; } return KERN_SUCCESS; } /* * * * * * * * * * * * * unused * * * * * * * * * */ void midilistener(int ignore) { kern_return_t r; char msgBuf[MIDI_MAX_MSG_SIZE]; msg_header_t *msg = (msg_header_t *)msgBuf; MIDIReplyFunctions funcs = {0, 0, 0, 0}; while (Listening) { funcs.dataReply = (MIDIDataReplyFunction)myDataReply; funcs.queueReply = (MIDIQueueReplyFunction)myQueueReply; msg->msg_local_port = ports; msg->msg_size = MIDI_MAX_MSG_SIZE; r = msg_receive(msg, 0, 0); /* This hangs waiting. If you want it not to, use 2nd arg of RCV_TIMEOUT - daj */ if (r == RCV_SUCCESS) r = MIDIHandleReply(msg, &funcs); } cthread_yield(); } int midilisten() { kern_return_t r; r = MIDIClearQueue(driverPort,ownerPort,unit); if (r != KERN_SUCCESS) return r; mutex_lock(listeningLock); Listening=1; mutex_unlock(listeningLock); cthread_detach(cthread_fork((cthread_fn_t)midilistener, (any_t)0)); return KERN_SUCCESS; } int midistoplistening() { kern_return_t r; mutex_lock(listeningLock); Listening=0; mutex_unlock(listeningLock); r = MIDIClearQueue(driverPort,ownerPort,unit); return r; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.