ftp.nice.ch/pub/next/unix/music/cm.sd.tar.gz#/midi/next-midi-3-0-block.c

This is next-midi-3-0-block.c in view mode; [Download] [Up]

/* interface to NeXT 3.0 midi driver.
 * if using Franz, compile by: cc -DEXCL next-2-0-midi.c -c -O
 *
 * This version blocks if too many bytes are written.
 */

#define EXCL 1

#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 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 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; 
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;
}

int midiopen(int port)		/* port=0 for A, 1 for B */
{
    kern_return_t r;
    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 */
    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;

    listeningLock = mutex_alloc();   /*  i should free these */
    schedulerLock = mutex_alloc();
    queueLock = mutex_alloc();     
    Listening = 0;
/*    startScheduler(); */
    return KERN_SUCCESS;
}

int midiclose()
{
    kern_return_t r;
 
    if (!driverPort || !ownerPort) return KERN_SUCCESS;
    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(), queuePort);
    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;
    
    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);
}

int midisettime(int time)
{
    quantaTime=time;
    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 waitForRoom(int size)
{
    int r;
    MIDIReplyFunctions recvStruct = {0};
    r = MIDIRequestQueueNotification(driverPort,ownerPort,
				     unit,queuePort,maxQueueSize/2);
    if (r != KERN_SUCCESS)
      return r;
    r = MIDIAwaitReply(queuePort,&recvStruct,MIDI_NO_TIMEOUT);
    /* THIS BLOCKS! */
    return r;
}

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;
    }
    for (;;) {
	r = MIDISendData(driverPort, ownerPort, unit, events, size);
	if (r == MIDI_ERROR_QUEUE_FULL) {
	  r = waitForRoom(size);
	  if (r != KERN_SUCCESS)
	    break;
	}
	else break;
      }
    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;
}

int midiflushxmit()
{
    return KERN_SUCCESS;
}

int midiflushrecv()
{
    return KERN_SUCCESS;
}

int midihush()
{
    return KERN_SUCCESS;
}

int midiallnotesoff()
{
    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)
{
}


These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.