[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[sc-dev] MIDI on linux



hi,

i've finished the linux/ALSA MIDI implementation; attached
are both the diff and the changed source file.

james, or someone else in the know: could you have a quick
look and verify i didn't mess up anything on OSX?

other kind OSXians could drop the changed source into
source/lang/LangSource, (try to) recompile and report any
problems ...

thanks!

<sk>
Index: source/lang/LangSource/SC_CoreMIDI.cpp
===================================================================
RCS file: /cvsroot/supercollider/SuperCollider3/source/lang/LangSource/SC_CoreMIDI.cpp,v
retrieving revision 1.25
diff -r1.25 SC_CoreMIDI.cpp
30,33d29
< #ifdef SC_DARWIN
< #include <CoreAudio/HostTime.h>
< #include <Carbon/Carbon.h>
< #include <CoreMIDI/CoreMIDI.h>
62,63d57
< MIDIClientRef gMIDIClient = 0;
< MIDIPortRef gMIDIInPort[kMaxMidiPorts], gMIDIOutPort[kMaxMidiPorts];
67c61,111
< void midiNotifyProc(const MIDINotification *msg, void* refCon)
---
> extern bool compiledOK;
> 
> // =====================================================================
> // Platform declarations (interface routines)
> // =====================================================================
> 
> static int initMIDI(int numIn, int numOut);
> static int disposeMIDI();
> static int restartMIDI();
> static void midiCleanUp();
> static int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late);
> static int listMIDIEndpoints(struct VMGlobals *g, PyrSlot *a);
> static int connectMIDIIn(int inputIndex, int uid);
> static int disconnectMIDIIn(int inputIndex, int uid);
> 
> // =====================================================================
> // Platform declarations (CoreMIDI)
> // =====================================================================
> 
> #if defined(SC_DARWIN)
> #include <CoreAudio/HostTime.h>
> #include <Carbon/Carbon.h>
> #include <CoreMIDI/CoreMIDI.h>
> 
> MIDIClientRef gMIDIClient = 0;
> MIDIPortRef gMIDIInPort[kMaxMidiPorts], gMIDIOutPort[kMaxMidiPorts];
> #endif // SC_DARWIN
> 
> // =====================================================================
> // Platform declarations (ALSA)
> // =====================================================================
> 
> #ifdef HAVE_CONFIG_H
> # include "config.h"
> #else
> # ifndef HAVE_ALSA
> #  define HAVE_ALSA 0
> # endif
> #endif
> 
> #if !defined(SC_DARWIN) && HAVE_ALSA
> #include <alsa/asoundlib.h>
> #include <pthread.h>
> #include <vector>
> #include <string.h>
> 
> static const size_t kAlsaMaxPacketSize = 3;
> static const size_t kAlsaMaxPortNameLen = 256;
> 
> // MIDI packet
> struct SC_AlsaMidiPacket
69c113,114
< }
---
> 	uint8 data[kAlsaMaxPacketSize];
> };
71c116,148
< extern bool compiledOK;
---
> // MIDI client state
> struct SC_AlsaMidiClient
> {
> 	snd_seq_t*			mHandle;
> 	int					mQueue;
> 	int					mInPorts[kMaxMidiPorts];
> 	int					mOutPorts[kMaxMidiPorts];
> 	snd_midi_event_t*	mEventToMidi;
> 	snd_midi_event_t*	mMidiToEvent;
> 	pthread_t			mInputThread;
> 	bool				mShouldBeRunning;
> 
> 	static void* inputThreadFunc(void*);
> 	void processEvent(snd_seq_event_t* evt);
> };
> 
> static SC_AlsaMidiClient* gMIDIClient = 0;
> 
> // Port description
> struct SC_AlsaMidiPort
> {
> 	SC_AlsaMidiPort()
> 		: uid(0)
> 	{ *name = 0; }
> 
> 	char		name[kAlsaMaxPortNameLen];
> 	int32		uid;
> };
> #endif // !SC_DARWIN && HAVE_ALSA
> 
> // =====================================================================
> // Platform implementation (CoreMIDI)
> // =====================================================================
72a150
> #if defined(SC_DARWIN)
138a217,220
> void midiNotifyProc(const MIDINotification *msg, void* refCon)
> {
> }
> 
149,150d230
< void midiCleanUp();
< 
209a290,306
> int disposeMIDI()
> {
>     OSStatus err = MIDIClientDispose(gMIDIClient);
>     if (err) {
>         post("error could not dispose MIDIClient \n");
>         return errFailed;
>     }
> 
>     return errNone;	
> }
> 
> int restartMIDI()
> {
>     MIDIRestart();
>     return errNone;	
> }
> 
228d324
< 
232a329,332
> int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late)
> {
> 	MIDIEndpointRef dest;
> 	MIDIObjectType mtype;
233a334,336
> 	MIDIObjectFindByUniqueID(destId, (MIDIObjectRef*)&dest, &mtype);
> 	if (mtype != kMIDIObjectType_Destination) return errFailed;		
> 	if (!dest) return errFailed;
235,236c338,353
< int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed);
< int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed)
---
>     MIDIPacketList mpktlist;
>     MIDIPacketList * pktlist = &mpktlist;
>     MIDIPacket * pk = MIDIPacketListInit(pktlist);
>     //lets add some latency
>     float  latency =  1000000 * late ; //ms to nano 
>     UInt64  utime = AudioConvertNanosToHostTime( AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) + (UInt64)latency);
>     ByteCount nData = (ByteCount) length;
>     pk->data[0] = (Byte) (hiStatus & 0xF0) | (loStatus & 0x0F); 
>     pk->data[1] = (Byte) aval;
>     pk->data[2] = (Byte) bval;
>     pk = MIDIPacketListAdd(pktlist, sizeof(struct MIDIPacketList) , pk,(MIDITimeStamp) utime,nData,pk->data);
>    	/*OSStatus error =*/ MIDISend(gMIDIOutPort[port],  dest, pktlist );
> 	return errNone;
> }
> 
> int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* a)
239d355
<     PyrSlot *a = g->sp;
366a483,1016
> }
> 
> int connectMIDIIn(int inputIndex, int uid)
> {
> 	MIDIEndpointRef src=0;
> 	MIDIObjectType mtype;
> 	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
> 	if (mtype != kMIDIObjectType_Source) return errFailed;
>        
> 	//pass the uid to the midiReadProc to identify the src   
> 	MIDIPortConnectSource(gMIDIInPort[inputIndex], src, (void*)uid);
> 
> 	return errNone;
> }
> 
> int disconnectMIDIIn(int inputIndex, int uid)
> {
> 	MIDIEndpointRef src=0;
> 	MIDIObjectType mtype;
> 	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
> 	if (mtype != kMIDIObjectType_Source) return errFailed;
> 
> 	MIDIPortDisconnectSource(gMIDIInPort[inputIndex], src);
> 
> 	return errNone;
> }
> #endif // SC_DARWIN
> 
> // =====================================================================
> // Platform implementation (ALSA)
> // =====================================================================
> 
> #if !defined(SC_DARWIN) && HAVE_ALSA
> void* SC_AlsaMidiClient::inputThreadFunc(void* arg)
> {
> 	SC_AlsaMidiClient* client = (SC_AlsaMidiClient*)arg;
> 	snd_seq_t* handle = client->mHandle;
> 	int npfd;
> 	struct pollfd *pfd;
> 
> 	npfd = snd_seq_poll_descriptors_count(handle, POLLIN);
> 	pfd = (struct pollfd*)malloc(npfd * sizeof(struct pollfd));
> 	if (pfd == 0) {
> 		post("MIDI (ALSA): memory allocation failure in input thread\n");
> 	}
> 
> 	snd_seq_poll_descriptors(handle, pfd, npfd, POLLIN);
> 
> 	while (client->mShouldBeRunning) {
> 		if (poll(pfd, npfd, 2000) > 0) { // 2s timeout
> 			for (int i=0; i < npfd; i++) {
> 				if (pfd[i].revents > 0) {
> 					do {
> 						snd_seq_event_t* evt;
> 						snd_seq_event_input(handle, &evt);
> 						client->processEvent(evt);
> 						snd_seq_free_event(evt);
> 					} while (snd_seq_event_input_pending(handle, 0) > 0);
> 				}
> 			}
> 		}
> 	}
> 
> 	free(pfd);
> 
> 	return 0;
> }
> 
> void SC_AlsaMidiClient::processEvent(snd_seq_event_t* evt)
> {
> 	pthread_mutex_lock (&gLangMutex);
> 	if (compiledOK) {
> 		VMGlobals *g = gMainVMGlobals;
> 		SC_AlsaMidiPacket pkt;
> 
> 		g->canCallOS = false; // cannot call the OS			
> 		++g->sp; SetObject(g->sp, s_midiin->u.classobj); // set the class MIDIIn
> 		//set arguments:
> 		++g->sp; SetInt(g->sp, evt->dest.port); // src
> 
> 		switch (evt->type) {
> 			case SND_SEQ_EVENT_NOTEOFF: // noteOff
> 				++g->sp; SetInt(g->sp, evt->data.note.channel);
> 				++g->sp; SetInt(g->sp, evt->data.note.note);
> 				++g->sp; SetInt(g->sp, evt->data.note.velocity);
> 				runInterpreter(g, s_midiNoteOffAction, 5);
> 				break;
> 			case SND_SEQ_EVENT_NOTEON: // noteOn 
> 				++g->sp; SetInt(g->sp, evt->data.note.channel);
> 				++g->sp; SetInt(g->sp, evt->data.note.note);
> 				++g->sp; SetInt(g->sp, evt->data.note.velocity);
> 				runInterpreter(g, evt->data.note.velocity ? s_midiNoteOnAction : s_midiNoteOffAction, 5);
> 				break;
> 			case SND_SEQ_EVENT_KEYPRESS: // polytouch
> 				++g->sp; SetInt(g->sp, evt->data.note.channel);
> 				++g->sp; SetInt(g->sp, evt->data.note.note);
> 				++g->sp; SetInt(g->sp, evt->data.note.velocity);
> 				runInterpreter(g, s_midiPolyTouchAction, 5);
> 				break;
> 			case SND_SEQ_EVENT_CONTROLLER: // control
> 				++g->sp; SetInt(g->sp, evt->data.control.channel);
> 				++g->sp; SetInt(g->sp, evt->data.control.param);
> 				++g->sp; SetInt(g->sp, evt->data.control.value);
> 				runInterpreter(g, s_midiControlAction, 5);
> 				break;
> 			case SND_SEQ_EVENT_PGMCHANGE: // program
> 				++g->sp; SetInt(g->sp, evt->data.control.channel);
> 				++g->sp; SetInt(g->sp, evt->data.control.param);
> 				runInterpreter(g, s_midiProgramAction, 4);
> 				break;
> 			case SND_SEQ_EVENT_CHANPRESS: // touch
> 				++g->sp; SetInt(g->sp, evt->data.control.channel);
> 				++g->sp; SetInt(g->sp, evt->data.control.param);
> 				runInterpreter(g, s_midiTouchAction, 4);
> 				break;
> 			case SND_SEQ_EVENT_PITCHBEND: // bend	
> 				++g->sp; SetInt(g->sp, evt->data.control.channel);
> 				++g->sp; SetInt(g->sp, evt->data.control.value + 8191);
> 				runInterpreter(g, s_midiBendAction, 4);
> 				break;
> 			case 0xF0: // sysex
> 				g->sp -= 2;
> 				break;  
> 			default :
> 				// convert to midi packet
> 				snd_midi_event_reset_decode(mEventToMidi);
> 				memset(pkt.data, 0, kAlsaMaxPacketSize);
> 				if (snd_midi_event_decode(mEventToMidi, pkt.data, kAlsaMaxPacketSize, evt) > 0) {
> 					++g->sp; SetInt(g->sp,  pkt.data[1]); // val1
> 					++g->sp; SetInt(g->sp,  pkt.data[2]); // val2
> 					runInterpreter(g, s_domidiaction, 5);
> 				} else {
> 					post("MIDI (ALSA): could not decode MIDI packet: %s\n", snd_strerror(errno));
> 					g->sp -= 2;
> 				}
> 				break;
> 				
> 		}
> 		g->canCallOS = false;
> 	}
> 	pthread_mutex_unlock (&gLangMutex); 
> 
> }
> 
> int initMIDI(int numIn, int numOut)
> {
> 	SC_AlsaMidiClient* client;
> 	int i;
> 
> 	if (gMIDIClient) midiCleanUp();
> 
>     numIn = sc_clip(numIn, 1, kMaxMidiPorts);
>     numOut = sc_clip(numOut, 1, kMaxMidiPorts);
> 
> 	gMIDIClient = client = new SC_AlsaMidiClient();
> 
> 	// initialize client handle
>     if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
> 		client->mHandle = 0;
> 		post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
>         return errFailed;
> 	}
> 
> 	snd_seq_set_client_name(client->mHandle, "SuperCollider");
>     
> 	// allocate i/o ports
>     for (i=0; i < numIn; i++) {
>         char str[32];
> 		int port;
> 
> 		sprintf(str, "in%d", i);
> 
> 		port = snd_seq_create_simple_port(
> 			client->mHandle, str,
> 			SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
>             SND_SEQ_PORT_TYPE_APPLICATION);
> 
> 		if (port < 0) {
> 			post("MIDI (ALSA): could not create MIDI in port %d: %s\n", i, snd_strerror(errno));
> 			break;
> 		}
> 
> 		client->mInPorts[i] = port;
>     }
> 
>     gNumMIDIInPorts = i;
>     
>     for (i=0; i < numOut; i++) {
>         char str[32];
> 		int port;
> 
>         sprintf(str, "out%d", i);
> 
> 		port = snd_seq_create_simple_port(
> 			client->mHandle, str,
> 			SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
>             SND_SEQ_PORT_TYPE_APPLICATION);
> 
> 		if (port < 0) {
>             gNumMIDIOutPorts = i;
>             post("MIDI (ALSA): could not create MIDI out port %d: %s\n", i, snd_strerror(errno));
> 			break;
>         }
> 
> 		client->mOutPorts[i] = port;
>     }
> 
>     gNumMIDIOutPorts = i;
> 
> 	// initialize queue
> 	client->mQueue = snd_seq_alloc_queue(client->mHandle);
> 	snd_seq_start_queue(client->mHandle, client->mQueue, 0);
> 	snd_seq_drain_output(client->mHandle);
> 	// snd_seq_set_client_pool_output(seqHandle, ??);
> 
> 	// initialize event en-/decoders
> 	if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
> 		client->mEventToMidi = 0;
> 		post("MIDI (ALSA): could not create MIDI decoder\n");
> 		return errFailed;
> 	}
> 
> 	if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
> 		client->mMidiToEvent = 0;
> 		post("MIDI (ALSA): could not create MIDI encoder\n");
> 		return errFailed;
> 	}
> 	
> 	snd_midi_event_no_status(client->mEventToMidi, 1);
> 	snd_midi_event_no_status(client->mMidiToEvent, 1);
> 
> 	// start input thread
> 	client->mShouldBeRunning = true;
> 	if (pthread_create(&client->mInputThread, 0, &SC_AlsaMidiClient::inputThreadFunc, client) != 0) {
> 		post("MIDI (ALSA): could not start input thread\n");
> 		return errFailed;
> 	}
> 
>     return errNone;
> }
> 
> int disposeMIDI()
> {
> 	midiCleanUp();
> 	return errNone;
> }
> 
> int restartMIDI()
> {
> 	return errNone;
> }
> 
> void midiCleanUp()
> {
> 	if (gMIDIClient->mHandle) {
> 		SC_AlsaMidiClient* client = gMIDIClient;
> 		gMIDIClient = 0;
> 
> 		client->mShouldBeRunning = false;
> 		pthread_join(client->mInputThread, 0);
> 
> 		snd_seq_remove_events_t *revt;
> 		snd_seq_remove_events_malloc(&revt);
> 		snd_seq_remove_events_set_queue(revt, client->mQueue);
> 		snd_seq_remove_events_set_condition(revt, SND_SEQ_REMOVE_OUTPUT|SND_SEQ_REMOVE_IGNORE_OFF);
> 		snd_seq_remove_events(client->mHandle, revt);
> 		snd_seq_remove_events_free(revt);
> 
> 		snd_seq_stop_queue(client->mHandle, client->mQueue, 0);
> 		snd_seq_free_queue(client->mHandle, client->mQueue);
> 
> 		if (client->mEventToMidi) {
> 			snd_midi_event_free(client->mEventToMidi);
> 		}
> 
> 		if (client->mMidiToEvent) {
> 			snd_midi_event_free(client->mMidiToEvent);
> 		}
> 
> 		snd_seq_close(client->mHandle);
> 	}
> 
> 	delete gMIDIClient;
> 	gMIDIClient = 0;
> }
> 
> int sendMIDI(int port, int uid, int length, int hiStatus, int loStatus, int aval, int bval, float late)
> {
> 	SC_AlsaMidiClient* client;
> 
> 	//post("MIDI (ALSA): send %x %x %d %d\n", hiStatus>>4, loStatus, aval, bval);
> 
> 	if ((client = gMIDIClient) && client->mHandle) {
> 		snd_seq_event_t evt;
> 		snd_seq_real_time time;
> 		SC_AlsaMidiPacket pkt;
> 
> 		snd_seq_ev_clear(&evt);
> 		pkt.data[0] = (hiStatus & 0xF0) | (loStatus & 0x0F);
> 		pkt.data[1] = (uint8)aval;
> 		pkt.data[2] = (uint8)bval;
> 		
> 		snd_midi_event_reset_encode(client->mMidiToEvent);
> 
> 		if (snd_midi_event_encode(client->mMidiToEvent, pkt.data, length, &evt) < 0) {
> 			post("MIDI (ALSA): could not encode midi data: %s\n", snd_strerror(errno));
> 			return errFailed;
> 		}
> 
> 		snd_seq_ev_set_source(&evt, client->mOutPorts[port]);
> 		if (uid == 0) {
> 			// send to all subscribed ports
> 			snd_seq_ev_set_subs(&evt);
> 		} else {
> 			// send to specific port
> 			snd_seq_ev_set_dest(&evt, uid >> 16, uid & 0xFFFF);
> 		}
> 		time.tv_sec = (unsigned)(late * 1.0e-6f);
> 		time.tv_nsec = (unsigned)(late * 1.0e3f);
> 		snd_seq_ev_schedule_real(&evt, client->mQueue, 1, &time);
> 		snd_seq_event_output_direct(client->mHandle, &evt);
> 
> 		return errNone;
> 	}
> 
> 	return errFailed;
> }
> 
> inline static bool SC_AlsaCheckPerm(snd_seq_port_info_t* pinfo, int bits)
> {
> 	int cap = snd_seq_port_info_get_capability(pinfo);
> 	return ((cap & bits) == bits) && !(cap & SND_SEQ_PORT_CAP_NO_EXPORT);
> }
> 
> int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* a)
> {
> 	snd_seq_t* seq;
> 	snd_seq_client_info_t* cinfo;
> 	snd_seq_port_info_t* pinfo;
> 
> 	if ((gMIDIClient == 0) || (gMIDIClient->mHandle == 0)) return errFailed;
> 
> 	seq = gMIDIClient->mHandle;
> 
> 	snd_seq_client_info_alloca(&cinfo);
> 	snd_seq_port_info_alloca(&pinfo);
> 	snd_seq_client_info_set_client(cinfo, -1);
> 
> 	std::vector<SC_AlsaMidiPort> srcPorts;
> 	std::vector<SC_AlsaMidiPort> dstPorts;
> 
> 	while (snd_seq_query_next_client(seq, cinfo) >= 0) {
> 		int cid = snd_seq_client_info_get_client(cinfo);
> 		const char* cname = snd_seq_client_info_get_name(cinfo);
> 
> 		if ((cid < 0) || (cid > 0xffff)) {
> 			post("MIDI (ALSA): client ID out of range.\n");
> 			return errFailed;
> 		}
> 
> 		snd_seq_port_info_set_client(pinfo, cid);
> 		snd_seq_port_info_set_port(pinfo, -1);
> 
> 		while (snd_seq_query_next_port(seq, pinfo) >= 0) {
> 			int pid = snd_seq_port_info_get_port(pinfo);
> 			const char* pname = snd_seq_port_info_get_name(pinfo);
> 
> 			if ((pid < 0) || (pid > 0xffff)) {
> 				post("MIDI (ALSA): port ID out of range.\n");
> 				return errFailed;
> 			}
> 
> 			if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) {
> 				// src port
> 				srcPorts.push_back(SC_AlsaMidiPort());
> 				snprintf(srcPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
> 				srcPorts.back().uid = (cid << 16) | pid;
> 				//post("MIDI (ALSA): src %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
> 			}
> 
> 			if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) {
> 				// dst port
> 				dstPorts.push_back(SC_AlsaMidiPort());
> 				snprintf(dstPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
> 				dstPorts.back().uid = (cid << 16) | pid;
> 				//post("MIDI (ALSA): dst %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
> 			}
> 		}
> 	}
> 
> 	int numSrc = srcPorts.size();
> 	int numDst = dstPorts.size();
> 
>     PyrObject* idarray = newPyrArray(g->gc, 6 * sizeof(PyrObject), 0 , true);
> 		SetObject(a, idarray);
> 
>     PyrObject* idarraySo = newPyrArray(g->gc, numSrc * sizeof(int32), 0 , true);
>         SetObject(idarray->slots+idarray->size++, idarraySo);
>         g->gc->GCWrite(idarray, idarraySo);
> 
>     PyrObject* devarraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
> 		SetObject(idarray->slots+idarray->size++, devarraySo);
> 		g->gc->GCWrite(idarray, devarraySo);
> 
> 	PyrObject* namearraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
>         SetObject(idarray->slots+idarray->size++, namearraySo);
>         g->gc->GCWrite(idarray, namearraySo);
> 
>     PyrObject* idarrayDe = newPyrArray(g->gc, numDst * sizeof(int32), 0 , true);
>         SetObject(idarray->slots+idarray->size++, idarrayDe);
>         g->gc->GCWrite(idarray, idarrayDe);
> 
>     PyrObject* namearrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
>         SetObject(idarray->slots+idarray->size++, namearrayDe);
>         g->gc->GCWrite(idarray, namearrayDe);
> 
>     PyrObject* devarrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
>         SetObject(idarray->slots+idarray->size++, devarrayDe);       
>         g->gc->GCWrite(idarray, devarrayDe);
> 
> 
>     for (int i=0; i<numSrc; ++i) {
> 		char* name = srcPorts[i].name;
> 
>         PyrString *string = newPyrString(g->gc, name, 0, true);
>         SetObject(namearraySo->slots+i, string);
>         namearraySo->size++;
>         g->gc->GCWrite(namearraySo, (PyrObject*)string);
> 
>         PyrString *devstring = newPyrString(g->gc, name, 0, true);
>         SetObject(devarraySo->slots+i, devstring);
>         devarraySo->size++;
>         g->gc->GCWrite(devarraySo, (PyrObject*)devstring);
> 
>         SetInt(idarraySo->slots+i, srcPorts[i].uid);
>         idarraySo->size++;
>     }
>     
>     for (int i=0; i<numDst; ++i) {
> 		char* name = dstPorts[i].name;
> 
>         PyrString *string = newPyrString(g->gc, name, 0, true);
>         SetObject(namearrayDe->slots+namearrayDe->size++, string);
>         g->gc->GCWrite(namearrayDe, (PyrObject*)string);
> 
>         PyrString *devstring = newPyrString(g->gc, name, 0, true);
>         SetObject(devarrayDe->slots+i, devstring);
> 		devarrayDe->size++;
>         g->gc->GCWrite(devarrayDe, (PyrObject*)devstring);
> 
>         SetInt(idarrayDe->slots+i, dstPorts[i].uid);
> 		idarrayDe->size++;
>     }
> 
> 	return errNone;
> }
> 
> static int SC_AlsaMidiDoConnectIn(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName)
> {
> 	if (!(gMIDIClient && gMIDIClient->mHandle)) return errFailed;
> 
> 	snd_seq_t* seq = gMIDIClient->mHandle;
> 	snd_seq_client_info_t* cinfo;
> 	snd_seq_port_subscribe_t* subs;
> 	snd_seq_addr_t src, dst;
> 
> 	snd_seq_client_info_alloca(&cinfo);
> 	if (snd_seq_get_client_info(seq, cinfo) < 0) {
> 		post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
> 		return errFailed;
> 	}
> 
> 	dst.client = snd_seq_client_info_get_client(cinfo);
> 	dst.port = gMIDIClient->mInPorts[inputIndex];
> 	src.client = uid >> 16;
> 	src.port = uid & 0xFFFF;
> 
> 	//post("MIDI (ALSA): connect ndx %d uid %u dst %d:%d src %d:%d\n", inputIndex, uid, dst.client, dst.port, src.client, src.port);
> 
> 	snd_seq_port_subscribe_alloca(&subs);	
> 	snd_seq_port_subscribe_set_sender(subs, &src);
> 	snd_seq_port_subscribe_set_dest(subs, &dst);
> 
> 	if ((*action)(seq, subs) < 0) {
> 		post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
> 		return errFailed;
> 	}
> 
> 	return errNone;
> }
> 
> int connectMIDIIn(int inputIndex, int uid)
> {
> 	return SC_AlsaMidiDoConnectIn(inputIndex, uid, &snd_seq_subscribe_port, "connect");
> }
> 
> int disconnectMIDIIn(int inputIndex, int uid)
> {
> 	return SC_AlsaMidiDoConnectIn(inputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
> }
> #endif // !SC_DARWIN && HAVE_ALSA
> 
> // =====================================================================
> // Platform implementation (fallback)
> // =====================================================================
> 
> #if !defined(SC_DARWIN) && !HAVE_ALSA
> int initMIDI(int numIn, int numOut)
> {
> 	return errNone;
> }
> 
> int disposeMIDI()
> {
> 	return errNone;
> }
> 
> int restartMIDI()
> {
> 	return errNone;
> }
> 
> void midiCleanUp()
> {
> }
> 
> int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late)
> {
> 	return errNone;
> }
> 
> int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* dst)
> {
> 	SetNil(dst);
369a1020,1029
> int connectMIDIIn(int inputIndex, int uid)
> {
> 	return errNone;
> }
> 
> int disconnectMIDIIn(int inputIndex, int uid)
> {
> 	return errNone;
> }
> #endif // !SC_DARWIN && !HAVE_ALSA
370a1031,1039
> // =====================================================================
> // Primitives
> // =====================================================================
> 
> int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed);
> int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed)
> {
> 	return listMIDIEndpoints(g, g->sp);
> }
387,397c1056
< 	
< 	MIDIEndpointRef src=0;
< 	MIDIObjectType mtype;
< 	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
< 	if (mtype != kMIDIObjectType_Source) return errFailed;
<        
<         //pass the uid to the midiReadProc to identify the src
<        
< 	MIDIPortConnectSource(gMIDIInPort[inputIndex], src, (void*)uid);
< 	
< 	return errNone;
---
> 	return connectMIDIIn(inputIndex, uid);
398a1058
> 
402c1062
<         PyrSlot *b = g->sp - 1;
---
> 	PyrSlot *b = g->sp - 1;
412,415c1072,1073
< 	MIDIEndpointRef src=0;
< 	MIDIObjectType mtype;
< 	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
< 	if (mtype != kMIDIObjectType_Source) return errFailed;
---
> 	return disconnectMIDIIn(inputIndex, uid);
> }
417,419c1075,1077
< 	MIDIPortDisconnectSource(gMIDIInPort[inputIndex], src);
< 	
< 	return errNone;
---
> // =====================================================================
> // Primitives (all platforms)
> // =====================================================================
421d1078
< }
437a1095
> 
443,448d1100
<     OSStatus err = MIDIClientDispose(gMIDIClient);
<     if (err) {
<         post("error could not dispose MIDIClient \n");
<         return errFailed;
<     }
<     return errNone;	
449a1102
> 	return disposeMIDI();
450a1104
> 
454,472c1108
<     MIDIRestart();
<     return errNone;	
< }
< 
< void sendmidi(int port, MIDIEndpointRef dest, int length, int hiStatus, int loStatus, int aval, int bval, float late);
< void sendmidi(int port, MIDIEndpointRef dest, int length, int hiStatus, int loStatus, int aval, int bval, float late)
< {
<     MIDIPacketList mpktlist;
<     MIDIPacketList * pktlist = &mpktlist;
<     MIDIPacket * pk = MIDIPacketListInit(pktlist);
<     //lets add some latency
<     float  latency =  1000000 * late ; //ms to nano 
<     UInt64  utime = AudioConvertNanosToHostTime( AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) + (UInt64)latency);
<     ByteCount nData = (ByteCount) length;
<     pk->data[0] = (Byte) (hiStatus & 0xF0) | (loStatus & 0x0F); 
<     pk->data[1] = (Byte) aval;
<     pk->data[2] = (Byte) bval;
<     pk = MIDIPacketListAdd(pktlist, sizeof(struct MIDIPacketList) , pk,(MIDITimeStamp) utime,nData,pk->data);
<    	/*OSStatus error =*/ MIDISend(gMIDIOutPort[port],  dest, pktlist );
---
> 	return restartMIDI();
497c1133
< 	if (outputIndex < 0 || outputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
---
> 	if (outputIndex < 0 || outputIndex >= gNumMIDIOutPorts) return errIndexOutOfRange;
514,522c1150
< 	MIDIEndpointRef dest;
< 	MIDIObjectType mtype;
< 	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&dest, &mtype);
< 	if (mtype != kMIDIObjectType_Destination) return errFailed;
< 		
< 	if (!dest) return errFailed;
< 
<     sendmidi(outputIndex, dest, length, hiStatus, loStatus, aval, bval, late);
<     return errNone;
---
>     return sendMIDI(outputIndex, uid, length, hiStatus, loStatus, aval, bval, late);
551,556c1179,1180
<     if(gMIDIClient) midiCleanUp();
< }
< #else // !SC_DARWIN
< void initMIDIPrimitives()
< {
< 	// sk: TODO: implement primitives
---
> 
>     if (gMIDIClient) midiCleanUp();
558d1181
< #endif // SC_DARWIN
/*
	SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
	http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
changes by jan trutzschler v. f. 9/9/2002
the midiReadProc calls doAction in the class MIDIIn.
with the arguments: inUid, status, chan, val1, val2
added prDisposeMIDIClient
added prRestartMIDI
19/9 call different actions,disconnect midiInPort, midiout: sendmidi
04/feb/03 prListMIDIEndpoints modification by Ron Kuivila added jt.
*/
#include "SCBase.h"
#include "VMGlobals.h"
#include "PyrSymbolTable.h"
#include "PyrInterpreter.h"
#include "PyrKernel.h"

#include "PyrPrimitive.h"
#include "PyrObjectProto.h"
#include "PyrPrimitiveProto.h"
#include "PyrKernelProto.h"
#include "SC_InlineUnaryOp.h"
#include "SC_InlineBinaryOp.h"
#include "PyrSched.h"
#include "GC.h"

PyrSymbol* s_domidiaction;
PyrSymbol* s_midiNoteOnAction;
PyrSymbol* s_midiNoteOffAction;
PyrSymbol* s_midiTouchAction;
PyrSymbol* s_midiControlAction;
PyrSymbol* s_midiPolyTouchAction;
PyrSymbol* s_midiProgramAction;
PyrSymbol* s_midiBendAction;
//jt
PyrSymbol * s_midiin;
PyrSymbol * s_numMIDIDev;
PyrSymbol * s_midiclient;
const int kMaxMidiPorts = 16;
int gNumMIDIInPorts = 0, gNumMIDIOutPorts = 0;
bool gMIDIInitialized = false;

extern bool compiledOK;

// =====================================================================
// Platform declarations (interface routines)
// =====================================================================

static int initMIDI(int numIn, int numOut);
static int disposeMIDI();
static int restartMIDI();
static void midiCleanUp();
static int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late);
static int listMIDIEndpoints(struct VMGlobals *g, PyrSlot *a);
static int connectMIDIIn(int inputIndex, int uid);
static int disconnectMIDIIn(int inputIndex, int uid);

// =====================================================================
// Platform declarations (CoreMIDI)
// =====================================================================

#if defined(SC_DARWIN)
#include <CoreAudio/HostTime.h>
#include <Carbon/Carbon.h>
#include <CoreMIDI/CoreMIDI.h>

MIDIClientRef gMIDIClient = 0;
MIDIPortRef gMIDIInPort[kMaxMidiPorts], gMIDIOutPort[kMaxMidiPorts];
#endif // SC_DARWIN

// =====================================================================
// Platform declarations (ALSA)
// =====================================================================

#ifdef HAVE_CONFIG_H
# include "config.h"
#else
# ifndef HAVE_ALSA
#  define HAVE_ALSA 0
# endif
#endif

#if !defined(SC_DARWIN) && HAVE_ALSA
#include <alsa/asoundlib.h>
#include <pthread.h>
#include <vector>
#include <string.h>

static const size_t kAlsaMaxPacketSize = 3;
static const size_t kAlsaMaxPortNameLen = 256;

// MIDI packet
struct SC_AlsaMidiPacket
{
	uint8 data[kAlsaMaxPacketSize];
};

// MIDI client state
struct SC_AlsaMidiClient
{
	snd_seq_t*			mHandle;
	int					mQueue;
	int					mInPorts[kMaxMidiPorts];
	int					mOutPorts[kMaxMidiPorts];
	snd_midi_event_t*	mEventToMidi;
	snd_midi_event_t*	mMidiToEvent;
	pthread_t			mInputThread;
	bool				mShouldBeRunning;

	static void* inputThreadFunc(void*);
	void processEvent(snd_seq_event_t* evt);
};

static SC_AlsaMidiClient* gMIDIClient = 0;

// Port description
struct SC_AlsaMidiPort
{
	SC_AlsaMidiPort()
		: uid(0)
	{ *name = 0; }

	char		name[kAlsaMaxPortNameLen];
	int32		uid;
};
#endif // !SC_DARWIN && HAVE_ALSA

// =====================================================================
// Platform implementation (CoreMIDI)
// =====================================================================

#if defined(SC_DARWIN)
static void midiProcessPacket(MIDIPacket *pkt, int uid)
{
 //jt
    if(pkt) {
     pthread_mutex_lock (&gLangMutex); //dont know if this is really needed/seems to be more stable..
		// it is needed  -jamesmcc
	if (compiledOK) {
			VMGlobals *g = gMainVMGlobals;
			uint8 status = pkt->data[0] & 0xF0;			
			uint8 chan = pkt->data[0] & 0x0F;
			g->canCallOS = false; // cannot call the OS
			
			++g->sp; SetObject(g->sp, s_midiin->u.classobj); // Set the class MIDIIn
			//set arguments: 
			++g->sp;SetInt(g->sp,  uid); //src
				// ++g->sp;  SetInt(g->sp, status); //status
			++g->sp;  SetInt(g->sp, chan); //chan
			switch (status) {
			case 0x80 : //noteOff
				++g->sp; SetInt(g->sp,  pkt->data[1]); //val1
				++g->sp; SetInt(g->sp,  pkt->data[2]); //val2
				runInterpreter(g, s_midiNoteOffAction, 5);
				break;
			case 0x90 : //noteOn 
				++g->sp; SetInt(g->sp,  pkt->data[1]); //val1
				++g->sp; SetInt(g->sp,  pkt->data[2]); //val2
				runInterpreter(g, pkt->data[2] ? s_midiNoteOnAction : s_midiNoteOffAction, 5);
				break;
			case 0xA0 : //polytouch
				++g->sp; SetInt(g->sp,  pkt->data[1]); //val1
				++g->sp; SetInt(g->sp,  pkt->data[2]); //val2
				runInterpreter(g, s_midiPolyTouchAction, 5);
				break;
			case 0xB0 : //control
				++g->sp; SetInt(g->sp,  pkt->data[1]); //val1
				++g->sp; SetInt(g->sp,  pkt->data[2]); //val2
				runInterpreter(g, s_midiControlAction, 5);
				break;
			case 0xC0 : //program
				++g->sp; SetInt(g->sp,  pkt->data[1]); //val1
				runInterpreter(g, s_midiProgramAction, 4);
				break;
			case 0xD0 : //touch
				++g->sp; SetInt(g->sp,  pkt->data[1]); //val1
				runInterpreter(g, s_midiTouchAction, 4);
				break;
			case 0xE0 : //bend	
				++g->sp; SetInt(g->sp,  (pkt->data[2] << 7) | pkt->data[1]); //val1
				runInterpreter(g, s_midiBendAction, 4);
				break;
			case 0xF0 :// ?
				g->sp -= 3; // nevermind
				break;  
			default :
				++g->sp; SetInt(g->sp,  pkt->data[1]); //val1
				++g->sp; SetInt(g->sp,  pkt->data[2]); //val2
				runInterpreter(g, s_domidiaction, 5);
				break;
				
		}
		g->canCallOS = false;
	}
    pthread_mutex_unlock (&gLangMutex); 
    }
}

void midiNotifyProc(const MIDINotification *msg, void* refCon)
{
}

static void midiReadProc(const MIDIPacketList *pktlist, void* readProcRefCon, void* srcConnRefCon)
{
    MIDIPacket *pkt = (MIDIPacket*)pktlist->packet;
    int uid = (int) srcConnRefCon;
    for (uint32 i=0; i<pktlist->numPackets; ++i) {
        midiProcessPacket(pkt, uid);
        pkt = MIDIPacketNext(pkt);
    }
}

int initMIDI(int numIn, int numOut)
{
	midiCleanUp();
    numIn = sc_clip(numIn, 1, kMaxMidiPorts);
    numOut = sc_clip(numOut, 1, kMaxMidiPorts);
    
    int enc = kCFStringEncodingMacRoman;
    CFAllocatorRef alloc = CFAllocatorGetDefault();
    
    CFStringRef clientName = CFStringCreateWithCString(alloc, "SuperCollider", enc);
    
    OSStatus err = MIDIClientCreate(clientName, midiNotifyProc, nil, &gMIDIClient);
    if (err) {
        post("Could not create MIDI client. error %d\n", err);
        return errFailed;
    }
    CFRelease(clientName);
    
    for (int i=0; i<numIn; ++i) {
        char str[32];
        sprintf(str, "in%d\n", i);
        CFStringRef inputPortName = CFStringCreateWithCString(alloc, str, enc);
        
        err = MIDIInputPortCreate(gMIDIClient, inputPortName, midiReadProc, &i, gMIDIInPort+i);
        if (err) {
            gNumMIDIInPorts = i;
            post("Could not create MIDI port %s. error %d\n", str, err);
            return errFailed;
        }
        CFRelease(inputPortName);
    }
	
	/*int n = MIDIGetNumberOfSources();
	printf("%d sources\n", n);
	for (i = 0; i < n; ++i) {
		MIDIEndpointRef src = MIDIGetSource(i);
		MIDIPortConnectSource(inPort, src, NULL);
	}*/
    
    gNumMIDIInPorts = numIn;
    
    for (int i=0; i<numOut; ++i) {
        char str[32];
        sprintf(str, "out%d\n", i);
        CFStringRef outputPortName = CFStringCreateWithCString(alloc, str, enc);
        
        err = MIDIOutputPortCreate(gMIDIClient, outputPortName, gMIDIOutPort+i);
        if (err) {
            gNumMIDIOutPorts = i;
            post("Could not create MIDI out port. error %d\n", err);
            return errFailed;
        }
        
        CFRelease(outputPortName);
    }
    gNumMIDIOutPorts = numIn;
    return errNone;
}

int disposeMIDI()
{
    OSStatus err = MIDIClientDispose(gMIDIClient);
    if (err) {
        post("error could not dispose MIDIClient \n");
        return errFailed;
    }

    return errNone;	
}

int restartMIDI()
{
    MIDIRestart();
    return errNone;	
}

void midiCleanUp()
{
    for (int i=0; i<gNumMIDIOutPorts; ++i) {
        MIDIPortDispose(gMIDIOutPort[i]);
    }
	gNumMIDIOutPorts = 0;
	
    for (int i=0; i<gNumMIDIInPorts; ++i) {
        MIDIPortDispose(gMIDIInPort[i]);
    }
	gNumMIDIInPorts = 0;
	
    if (gMIDIClient) {
        MIDIClientDispose(gMIDIClient);
		gMIDIClient = 0;
    }
}

void midiListEndpoints()
{
}

int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late)
{
	MIDIEndpointRef dest;
	MIDIObjectType mtype;

	MIDIObjectFindByUniqueID(destId, (MIDIObjectRef*)&dest, &mtype);
	if (mtype != kMIDIObjectType_Destination) return errFailed;		
	if (!dest) return errFailed;

    MIDIPacketList mpktlist;
    MIDIPacketList * pktlist = &mpktlist;
    MIDIPacket * pk = MIDIPacketListInit(pktlist);
    //lets add some latency
    float  latency =  1000000 * late ; //ms to nano 
    UInt64  utime = AudioConvertNanosToHostTime( AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) + (UInt64)latency);
    ByteCount nData = (ByteCount) length;
    pk->data[0] = (Byte) (hiStatus & 0xF0) | (loStatus & 0x0F); 
    pk->data[1] = (Byte) aval;
    pk->data[2] = (Byte) bval;
    pk = MIDIPacketListAdd(pktlist, sizeof(struct MIDIPacketList) , pk,(MIDITimeStamp) utime,nData,pk->data);
   	/*OSStatus error =*/ MIDISend(gMIDIOutPort[port],  dest, pktlist );
	return errNone;
}

int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* a)
{
    OSStatus error;
    int numSrc = MIDIGetNumberOfSources();
    int numDst = MIDIGetNumberOfDestinations();

    PyrObject* idarray = newPyrArray(g->gc, 6 * sizeof(PyrObject), 0 , true);
		SetObject(a, idarray);

    PyrObject* idarraySo = newPyrArray(g->gc, numSrc * sizeof(SInt32), 0 , true);
        SetObject(idarray->slots+idarray->size++, idarraySo);
        g->gc->GCWrite(idarray, idarraySo);

    PyrObject* devarraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
        SetObject(idarray->slots+idarray->size++, devarraySo);
        g->gc->GCWrite(idarray, devarraySo);

        PyrObject* namearraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
        SetObject(idarray->slots+idarray->size++, namearraySo);
        g->gc->GCWrite(idarray, namearraySo);

    PyrObject* idarrayDe = newPyrArray(g->gc, numDst * sizeof(SInt32), 0 , true);
        SetObject(idarray->slots+idarray->size++, idarrayDe);
        g->gc->GCWrite(idarray, idarrayDe);

    PyrObject* namearrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
        SetObject(idarray->slots+idarray->size++, namearrayDe);
        g->gc->GCWrite(idarray, namearrayDe);

    PyrObject* devarrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
        SetObject(idarray->slots+idarray->size++, devarrayDe);       
        g->gc->GCWrite(idarray, devarrayDe);


    for (int i=0; i<numSrc; ++i) {
        MIDIEndpointRef src = MIDIGetSource(i);
        SInt32 id;
        MIDIObjectGetIntegerProperty(src, kMIDIPropertyUniqueID, &id);

        MIDIEntityRef ent;
        error = MIDIEndpointGetEntity(src, &ent);

        CFStringRef devname, endname;
        char cendname[1024], cdevname[1024];

        // Virtual sources don't have entities
        if(error)
        {
            MIDIObjectGetStringProperty(src, kMIDIPropertyName, &devname);
            MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endname);
            CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
            CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
        }
        else
        {
            MIDIDeviceRef dev;

            MIDIEntityGetDevice(ent, &dev);
            MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &devname);
            MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endname);
            CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
            CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
        }

        PyrString *string = newPyrString(g->gc, cendname, 0, true);
        SetObject(namearraySo->slots+i, string);
        namearraySo->size++;
        g->gc->GCWrite(namearraySo, (PyrObject*)string);

        PyrString *devstring = newPyrString(g->gc, cdevname, 0, true);
        SetObject(devarraySo->slots+i, devstring);
        devarraySo->size++;
        g->gc->GCWrite(devarraySo, (PyrObject*)devstring);

        SetInt(idarraySo->slots+i, id);
        idarraySo->size++;

        CFRelease(devname);
        CFRelease(endname);
    }
    


//      post("numDst %d\n",  numDst);
    for (int i=0; i<numDst; ++i) {
        MIDIEndpointRef dst = MIDIGetDestination(i);
        SInt32 id;
        MIDIObjectGetIntegerProperty(dst, kMIDIPropertyUniqueID, &id);

        MIDIEntityRef ent;
        error = MIDIEndpointGetEntity(dst, &ent);

        CFStringRef devname, endname;
        char cendname[1024], cdevname[1024];

        // Virtual destinations don't have entities either
        if(error)
        {
            MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &devname);
            MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &endname);
            CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
            CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);

        }
        else
        {
            MIDIDeviceRef dev;

            MIDIEntityGetDevice(ent, &dev);
            MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &devname);
            MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &endname);
            CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
            CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
        }

        PyrString *string = newPyrString(g->gc, cendname, 0, true);
        SetObject(namearrayDe->slots+namearrayDe->size++, string);
        g->gc->GCWrite(namearrayDe, (PyrObject*)string);

        PyrString *devstring = newPyrString(g->gc, cdevname, 0, true);

        SetObject(devarrayDe->slots+devarrayDe->size++, devstring);
        g->gc->GCWrite(devarrayDe, (PyrObject*)devstring);

        SetInt(idarrayDe->slots+idarrayDe->size++, id);

        CFRelease(devname);
        CFRelease(endname);

    }
}

int connectMIDIIn(int inputIndex, int uid)
{
	MIDIEndpointRef src=0;
	MIDIObjectType mtype;
	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
	if (mtype != kMIDIObjectType_Source) return errFailed;
       
	//pass the uid to the midiReadProc to identify the src   
	MIDIPortConnectSource(gMIDIInPort[inputIndex], src, (void*)uid);

	return errNone;
}

int disconnectMIDIIn(int inputIndex, int uid)
{
	MIDIEndpointRef src=0;
	MIDIObjectType mtype;
	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
	if (mtype != kMIDIObjectType_Source) return errFailed;

	MIDIPortDisconnectSource(gMIDIInPort[inputIndex], src);

	return errNone;
}
#endif // SC_DARWIN

// =====================================================================
// Platform implementation (ALSA)
// =====================================================================

#if !defined(SC_DARWIN) && HAVE_ALSA
void* SC_AlsaMidiClient::inputThreadFunc(void* arg)
{
	SC_AlsaMidiClient* client = (SC_AlsaMidiClient*)arg;
	snd_seq_t* handle = client->mHandle;
	int npfd;
	struct pollfd *pfd;

	npfd = snd_seq_poll_descriptors_count(handle, POLLIN);
	pfd = (struct pollfd*)malloc(npfd * sizeof(struct pollfd));
	if (pfd == 0) {
		post("MIDI (ALSA): memory allocation failure in input thread\n");
	}

	snd_seq_poll_descriptors(handle, pfd, npfd, POLLIN);

	while (client->mShouldBeRunning) {
		if (poll(pfd, npfd, 2000) > 0) { // 2s timeout
			for (int i=0; i < npfd; i++) {
				if (pfd[i].revents > 0) {
					do {
						snd_seq_event_t* evt;
						snd_seq_event_input(handle, &evt);
						client->processEvent(evt);
						snd_seq_free_event(evt);
					} while (snd_seq_event_input_pending(handle, 0) > 0);
				}
			}
		}
	}

	free(pfd);

	return 0;
}

void SC_AlsaMidiClient::processEvent(snd_seq_event_t* evt)
{
	pthread_mutex_lock (&gLangMutex);
	if (compiledOK) {
		VMGlobals *g = gMainVMGlobals;
		SC_AlsaMidiPacket pkt;

		g->canCallOS = false; // cannot call the OS			
		++g->sp; SetObject(g->sp, s_midiin->u.classobj); // set the class MIDIIn
		//set arguments:
		++g->sp; SetInt(g->sp, evt->dest.port); // src

		switch (evt->type) {
			case SND_SEQ_EVENT_NOTEOFF: // noteOff
				++g->sp; SetInt(g->sp, evt->data.note.channel);
				++g->sp; SetInt(g->sp, evt->data.note.note);
				++g->sp; SetInt(g->sp, evt->data.note.velocity);
				runInterpreter(g, s_midiNoteOffAction, 5);
				break;
			case SND_SEQ_EVENT_NOTEON: // noteOn 
				++g->sp; SetInt(g->sp, evt->data.note.channel);
				++g->sp; SetInt(g->sp, evt->data.note.note);
				++g->sp; SetInt(g->sp, evt->data.note.velocity);
				runInterpreter(g, evt->data.note.velocity ? s_midiNoteOnAction : s_midiNoteOffAction, 5);
				break;
			case SND_SEQ_EVENT_KEYPRESS: // polytouch
				++g->sp; SetInt(g->sp, evt->data.note.channel);
				++g->sp; SetInt(g->sp, evt->data.note.note);
				++g->sp; SetInt(g->sp, evt->data.note.velocity);
				runInterpreter(g, s_midiPolyTouchAction, 5);
				break;
			case SND_SEQ_EVENT_CONTROLLER: // control
				++g->sp; SetInt(g->sp, evt->data.control.channel);
				++g->sp; SetInt(g->sp, evt->data.control.param);
				++g->sp; SetInt(g->sp, evt->data.control.value);
				runInterpreter(g, s_midiControlAction, 5);
				break;
			case SND_SEQ_EVENT_PGMCHANGE: // program
				++g->sp; SetInt(g->sp, evt->data.control.channel);
				++g->sp; SetInt(g->sp, evt->data.control.param);
				runInterpreter(g, s_midiProgramAction, 4);
				break;
			case SND_SEQ_EVENT_CHANPRESS: // touch
				++g->sp; SetInt(g->sp, evt->data.control.channel);
				++g->sp; SetInt(g->sp, evt->data.control.param);
				runInterpreter(g, s_midiTouchAction, 4);
				break;
			case SND_SEQ_EVENT_PITCHBEND: // bend	
				++g->sp; SetInt(g->sp, evt->data.control.channel);
				++g->sp; SetInt(g->sp, evt->data.control.value + 8191);
				runInterpreter(g, s_midiBendAction, 4);
				break;
			case 0xF0: // sysex
				g->sp -= 2;
				break;  
			default :
				// convert to midi packet
				snd_midi_event_reset_decode(mEventToMidi);
				memset(pkt.data, 0, kAlsaMaxPacketSize);
				if (snd_midi_event_decode(mEventToMidi, pkt.data, kAlsaMaxPacketSize, evt) > 0) {
					++g->sp; SetInt(g->sp,  pkt.data[1]); // val1
					++g->sp; SetInt(g->sp,  pkt.data[2]); // val2
					runInterpreter(g, s_domidiaction, 5);
				} else {
					post("MIDI (ALSA): could not decode MIDI packet: %s\n", snd_strerror(errno));
					g->sp -= 2;
				}
				break;
				
		}
		g->canCallOS = false;
	}
	pthread_mutex_unlock (&gLangMutex); 

}

int initMIDI(int numIn, int numOut)
{
	SC_AlsaMidiClient* client;
	int i;

	if (gMIDIClient) midiCleanUp();

    numIn = sc_clip(numIn, 1, kMaxMidiPorts);
    numOut = sc_clip(numOut, 1, kMaxMidiPorts);

	gMIDIClient = client = new SC_AlsaMidiClient();

	// initialize client handle
    if (snd_seq_open(&client->mHandle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
		client->mHandle = 0;
		post("MIDI (ALSA): could not open ALSA sequencer: %s\n", snd_strerror(errno));
        return errFailed;
	}

	snd_seq_set_client_name(client->mHandle, "SuperCollider");
    
	// allocate i/o ports
    for (i=0; i < numIn; i++) {
        char str[32];
		int port;

		sprintf(str, "in%d", i);

		port = snd_seq_create_simple_port(
			client->mHandle, str,
			SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
            SND_SEQ_PORT_TYPE_APPLICATION);

		if (port < 0) {
			post("MIDI (ALSA): could not create MIDI in port %d: %s\n", i, snd_strerror(errno));
			break;
		}

		client->mInPorts[i] = port;
    }

    gNumMIDIInPorts = i;
    
    for (i=0; i < numOut; i++) {
        char str[32];
		int port;

        sprintf(str, "out%d", i);

		port = snd_seq_create_simple_port(
			client->mHandle, str,
			SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
            SND_SEQ_PORT_TYPE_APPLICATION);

		if (port < 0) {
            gNumMIDIOutPorts = i;
            post("MIDI (ALSA): could not create MIDI out port %d: %s\n", i, snd_strerror(errno));
			break;
        }

		client->mOutPorts[i] = port;
    }

    gNumMIDIOutPorts = i;

	// initialize queue
	client->mQueue = snd_seq_alloc_queue(client->mHandle);
	snd_seq_start_queue(client->mHandle, client->mQueue, 0);
	snd_seq_drain_output(client->mHandle);
	// snd_seq_set_client_pool_output(seqHandle, ??);

	// initialize event en-/decoders
	if (snd_midi_event_new(32, &client->mEventToMidi) < 0) {
		client->mEventToMidi = 0;
		post("MIDI (ALSA): could not create MIDI decoder\n");
		return errFailed;
	}

	if (snd_midi_event_new(32, &client->mMidiToEvent) < 0) {
		client->mMidiToEvent = 0;
		post("MIDI (ALSA): could not create MIDI encoder\n");
		return errFailed;
	}
	
	snd_midi_event_no_status(client->mEventToMidi, 1);
	snd_midi_event_no_status(client->mMidiToEvent, 1);

	// start input thread
	client->mShouldBeRunning = true;
	if (pthread_create(&client->mInputThread, 0, &SC_AlsaMidiClient::inputThreadFunc, client) != 0) {
		post("MIDI (ALSA): could not start input thread\n");
		return errFailed;
	}

    return errNone;
}

int disposeMIDI()
{
	midiCleanUp();
	return errNone;
}

int restartMIDI()
{
	return errNone;
}

void midiCleanUp()
{
	if (gMIDIClient->mHandle) {
		SC_AlsaMidiClient* client = gMIDIClient;
		gMIDIClient = 0;

		client->mShouldBeRunning = false;
		pthread_join(client->mInputThread, 0);

		snd_seq_remove_events_t *revt;
		snd_seq_remove_events_malloc(&revt);
		snd_seq_remove_events_set_queue(revt, client->mQueue);
		snd_seq_remove_events_set_condition(revt, SND_SEQ_REMOVE_OUTPUT|SND_SEQ_REMOVE_IGNORE_OFF);
		snd_seq_remove_events(client->mHandle, revt);
		snd_seq_remove_events_free(revt);

		snd_seq_stop_queue(client->mHandle, client->mQueue, 0);
		snd_seq_free_queue(client->mHandle, client->mQueue);

		if (client->mEventToMidi) {
			snd_midi_event_free(client->mEventToMidi);
		}

		if (client->mMidiToEvent) {
			snd_midi_event_free(client->mMidiToEvent);
		}

		snd_seq_close(client->mHandle);
	}

	delete gMIDIClient;
	gMIDIClient = 0;
}

int sendMIDI(int port, int uid, int length, int hiStatus, int loStatus, int aval, int bval, float late)
{
	SC_AlsaMidiClient* client;

	//post("MIDI (ALSA): send %x %x %d %d\n", hiStatus>>4, loStatus, aval, bval);

	if ((client = gMIDIClient) && client->mHandle) {
		snd_seq_event_t evt;
		snd_seq_real_time time;
		SC_AlsaMidiPacket pkt;

		snd_seq_ev_clear(&evt);
		pkt.data[0] = (hiStatus & 0xF0) | (loStatus & 0x0F);
		pkt.data[1] = (uint8)aval;
		pkt.data[2] = (uint8)bval;
		
		snd_midi_event_reset_encode(client->mMidiToEvent);

		if (snd_midi_event_encode(client->mMidiToEvent, pkt.data, length, &evt) < 0) {
			post("MIDI (ALSA): could not encode midi data: %s\n", snd_strerror(errno));
			return errFailed;
		}

		snd_seq_ev_set_source(&evt, client->mOutPorts[port]);
		if (uid == 0) {
			// send to all subscribed ports
			snd_seq_ev_set_subs(&evt);
		} else {
			// send to specific port
			snd_seq_ev_set_dest(&evt, uid >> 16, uid & 0xFFFF);
		}
		time.tv_sec = (unsigned)(late * 1.0e-6f);
		time.tv_nsec = (unsigned)(late * 1.0e3f);
		snd_seq_ev_schedule_real(&evt, client->mQueue, 1, &time);
		snd_seq_event_output_direct(client->mHandle, &evt);

		return errNone;
	}

	return errFailed;
}

inline static bool SC_AlsaCheckPerm(snd_seq_port_info_t* pinfo, int bits)
{
	int cap = snd_seq_port_info_get_capability(pinfo);
	return ((cap & bits) == bits) && !(cap & SND_SEQ_PORT_CAP_NO_EXPORT);
}

int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* a)
{
	snd_seq_t* seq;
	snd_seq_client_info_t* cinfo;
	snd_seq_port_info_t* pinfo;

	if ((gMIDIClient == 0) || (gMIDIClient->mHandle == 0)) return errFailed;

	seq = gMIDIClient->mHandle;

	snd_seq_client_info_alloca(&cinfo);
	snd_seq_port_info_alloca(&pinfo);
	snd_seq_client_info_set_client(cinfo, -1);

	std::vector<SC_AlsaMidiPort> srcPorts;
	std::vector<SC_AlsaMidiPort> dstPorts;

	while (snd_seq_query_next_client(seq, cinfo) >= 0) {
		int cid = snd_seq_client_info_get_client(cinfo);
		const char* cname = snd_seq_client_info_get_name(cinfo);

		if ((cid < 0) || (cid > 0xffff)) {
			post("MIDI (ALSA): client ID out of range.\n");
			return errFailed;
		}

		snd_seq_port_info_set_client(pinfo, cid);
		snd_seq_port_info_set_port(pinfo, -1);

		while (snd_seq_query_next_port(seq, pinfo) >= 0) {
			int pid = snd_seq_port_info_get_port(pinfo);
			const char* pname = snd_seq_port_info_get_name(pinfo);

			if ((pid < 0) || (pid > 0xffff)) {
				post("MIDI (ALSA): port ID out of range.\n");
				return errFailed;
			}

			if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) {
				// src port
				srcPorts.push_back(SC_AlsaMidiPort());
				snprintf(srcPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
				srcPorts.back().uid = (cid << 16) | pid;
				//post("MIDI (ALSA): src %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
			}

			if (SC_AlsaCheckPerm(pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE)) {
				// dst port
				dstPorts.push_back(SC_AlsaMidiPort());
				snprintf(dstPorts.back().name, kAlsaMaxPortNameLen, "%s-%s", cname, pname);
				dstPorts.back().uid = (cid << 16) | pid;
				//post("MIDI (ALSA): dst %s-%s %d:%d %u\n", cname, pname, cid, pid, srcPorts.back().uid);
			}
		}
	}

	int numSrc = srcPorts.size();
	int numDst = dstPorts.size();

    PyrObject* idarray = newPyrArray(g->gc, 6 * sizeof(PyrObject), 0 , true);
		SetObject(a, idarray);

    PyrObject* idarraySo = newPyrArray(g->gc, numSrc * sizeof(int32), 0 , true);
        SetObject(idarray->slots+idarray->size++, idarraySo);
        g->gc->GCWrite(idarray, idarraySo);

    PyrObject* devarraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
		SetObject(idarray->slots+idarray->size++, devarraySo);
		g->gc->GCWrite(idarray, devarraySo);

	PyrObject* namearraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
        SetObject(idarray->slots+idarray->size++, namearraySo);
        g->gc->GCWrite(idarray, namearraySo);

    PyrObject* idarrayDe = newPyrArray(g->gc, numDst * sizeof(int32), 0 , true);
        SetObject(idarray->slots+idarray->size++, idarrayDe);
        g->gc->GCWrite(idarray, idarrayDe);

    PyrObject* namearrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
        SetObject(idarray->slots+idarray->size++, namearrayDe);
        g->gc->GCWrite(idarray, namearrayDe);

    PyrObject* devarrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
        SetObject(idarray->slots+idarray->size++, devarrayDe);       
        g->gc->GCWrite(idarray, devarrayDe);


    for (int i=0; i<numSrc; ++i) {
		char* name = srcPorts[i].name;

        PyrString *string = newPyrString(g->gc, name, 0, true);
        SetObject(namearraySo->slots+i, string);
        namearraySo->size++;
        g->gc->GCWrite(namearraySo, (PyrObject*)string);

        PyrString *devstring = newPyrString(g->gc, name, 0, true);
        SetObject(devarraySo->slots+i, devstring);
        devarraySo->size++;
        g->gc->GCWrite(devarraySo, (PyrObject*)devstring);

        SetInt(idarraySo->slots+i, srcPorts[i].uid);
        idarraySo->size++;
    }
    
    for (int i=0; i<numDst; ++i) {
		char* name = dstPorts[i].name;

        PyrString *string = newPyrString(g->gc, name, 0, true);
        SetObject(namearrayDe->slots+namearrayDe->size++, string);
        g->gc->GCWrite(namearrayDe, (PyrObject*)string);

        PyrString *devstring = newPyrString(g->gc, name, 0, true);
        SetObject(devarrayDe->slots+i, devstring);
		devarrayDe->size++;
        g->gc->GCWrite(devarrayDe, (PyrObject*)devstring);

        SetInt(idarrayDe->slots+i, dstPorts[i].uid);
		idarrayDe->size++;
    }

	return errNone;
}

static int SC_AlsaMidiDoConnectIn(int inputIndex, int uid, int (*action)(snd_seq_t*, snd_seq_port_subscribe_t*), const char* actionName)
{
	if (!(gMIDIClient && gMIDIClient->mHandle)) return errFailed;

	snd_seq_t* seq = gMIDIClient->mHandle;
	snd_seq_client_info_t* cinfo;
	snd_seq_port_subscribe_t* subs;
	snd_seq_addr_t src, dst;

	snd_seq_client_info_alloca(&cinfo);
	if (snd_seq_get_client_info(seq, cinfo) < 0) {
		post("MIDI (ALSA): could not get client info: %s\n", snd_strerror(errno));
		return errFailed;
	}

	dst.client = snd_seq_client_info_get_client(cinfo);
	dst.port = gMIDIClient->mInPorts[inputIndex];
	src.client = uid >> 16;
	src.port = uid & 0xFFFF;

	//post("MIDI (ALSA): connect ndx %d uid %u dst %d:%d src %d:%d\n", inputIndex, uid, dst.client, dst.port, src.client, src.port);

	snd_seq_port_subscribe_alloca(&subs);	
	snd_seq_port_subscribe_set_sender(subs, &src);
	snd_seq_port_subscribe_set_dest(subs, &dst);

	if ((*action)(seq, subs) < 0) {
		post("MIDI (ALSA): %s failed (%s)\n", actionName, snd_strerror(errno));
		return errFailed;
	}

	return errNone;
}

int connectMIDIIn(int inputIndex, int uid)
{
	return SC_AlsaMidiDoConnectIn(inputIndex, uid, &snd_seq_subscribe_port, "connect");
}

int disconnectMIDIIn(int inputIndex, int uid)
{
	return SC_AlsaMidiDoConnectIn(inputIndex, uid, &snd_seq_unsubscribe_port, "disconnect");
}
#endif // !SC_DARWIN && HAVE_ALSA

// =====================================================================
// Platform implementation (fallback)
// =====================================================================

#if !defined(SC_DARWIN) && !HAVE_ALSA
int initMIDI(int numIn, int numOut)
{
	return errNone;
}

int disposeMIDI()
{
	return errNone;
}

int restartMIDI()
{
	return errNone;
}

void midiCleanUp()
{
}

int sendMIDI(int port, int destId, int length, int hiStatus, int loStatus, int aval, int bval, float late)
{
	return errNone;
}

int listMIDIEndpoints(struct VMGlobals *g, PyrSlot* dst)
{
	SetNil(dst);
    return errNone;
}

int connectMIDIIn(int inputIndex, int uid)
{
	return errNone;
}

int disconnectMIDIIn(int inputIndex, int uid)
{
	return errNone;
}
#endif // !SC_DARWIN && !HAVE_ALSA

// =====================================================================
// Primitives
// =====================================================================

int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed);
int prListMIDIEndpoints(struct VMGlobals *g, int numArgsPushed)
{
	return listMIDIEndpoints(g, g->sp);
}

int prConnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
int prConnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
{
	//PyrSlot *a = g->sp - 2;
	PyrSlot *b = g->sp - 1;
	PyrSlot *c = g->sp;
        
	int err, inputIndex, uid;
	err = slotIntVal(b, &inputIndex);
	if (err) return errWrongType;
	if (inputIndex < 0 || inputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
	
	err = slotIntVal(c, &uid);
	if (err) return errWrongType;

	return connectMIDIIn(inputIndex, uid);
}

int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed);
int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
{
	PyrSlot *b = g->sp - 1;
	PyrSlot *c = g->sp;
        
	int err, inputIndex, uid;
	err = slotIntVal(b, &inputIndex);
	if (err) return err;
	if (inputIndex < 0 || inputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
	err = slotIntVal(c, &uid);
	if (err) return err;

	return disconnectMIDIIn(inputIndex, uid);
}

// =====================================================================
// Primitives (all platforms)
// =====================================================================

int prInitMIDI(struct VMGlobals *g, int numArgsPushed);
int prInitMIDI(struct VMGlobals *g, int numArgsPushed)
{
	//PyrSlot *a = g->sp - 2;
	PyrSlot *b = g->sp - 1;
	PyrSlot *c = g->sp;
        
	int err, numIn, numOut;
	err = slotIntVal(b, &numIn);
	if (err) return errWrongType;
	
	err = slotIntVal(c, &numOut);
	if (err) return errWrongType;
	
	return initMIDI(numIn, numOut);
}

int prDisposeMIDIClient(VMGlobals *g, int numArgsPushed);
int prDisposeMIDIClient(VMGlobals *g, int numArgsPushed)
{
    PyrSlot *a;
    a = g->sp - 1;

	return disposeMIDI();
}

int prRestartMIDI(VMGlobals *g, int numArgsPushed);
int prRestartMIDI(VMGlobals *g, int numArgsPushed)
{
	return restartMIDI();
}

int prSendMIDIOut(struct VMGlobals *g, int numArgsPushed);
int prSendMIDIOut(struct VMGlobals *g, int numArgsPushed)
{
        //port, uid, len, hiStatus, loStatus, a, b, latency
	//PyrSlot *m = g->sp - 8;
	PyrSlot *p = g->sp - 7;
        
	PyrSlot *u = g->sp - 6;
	PyrSlot *l = g->sp - 5;
        
	PyrSlot *his = g->sp - 4;
	PyrSlot *los = g->sp - 3;
        
    PyrSlot *a = g->sp - 2;
	PyrSlot *b = g->sp - 1;
    PyrSlot *plat = g->sp;

        
	int err, outputIndex, uid, length, hiStatus, loStatus, aval, bval;
    float late;
	err = slotIntVal(p, &outputIndex);
	if (err) return err;
	if (outputIndex < 0 || outputIndex >= gNumMIDIOutPorts) return errIndexOutOfRange;
	
	err = slotIntVal(u, &uid);
	if (err) return err;
        err = slotIntVal(l, &length);
	if (err) return err;
        err = slotIntVal(his, &hiStatus);
	if (err) return err;
        err = slotIntVal(los, &loStatus);
	if (err) return err;
        err = slotIntVal(a, &aval);
	if (err) return err;
        err = slotIntVal(b, &bval);
	if (err) return err;
        err = slotFloatVal(plat, &late);
	if (err) return err;

    return sendMIDI(outputIndex, uid, length, hiStatus, loStatus, aval, bval, late);
}

void initMIDIPrimitives()
{
	int base, index;
        
	base = nextPrimitiveIndex();
	index = 0;
        
	s_midiin = getsym("MIDIIn");
    s_domidiaction = getsym("doAction");
    s_midiNoteOnAction = getsym("doNoteOnAction");
    s_midiNoteOffAction = getsym("doNoteOffAction");
    s_midiTouchAction = getsym("doTouchAction");
    s_midiControlAction = getsym("doControlAction");
    s_midiPolyTouchAction = getsym("doPolyTouchAction");
    s_midiProgramAction = getsym("doProgramAction");
    s_midiBendAction = getsym("doBendAction");
    s_numMIDIDev = getsym("prSetNumberOfDevices");
    s_midiclient = getsym("MIDIClient");
       definePrimitive(base, index++, "_ListMIDIEndpoints", prListMIDIEndpoints, 1, 0);	
	definePrimitive(base, index++, "_InitMIDI", prInitMIDI, 3, 0);	
	definePrimitive(base, index++, "_ConnectMIDIIn", prConnectMIDIIn, 3, 0);
	definePrimitive(base, index++, "_DisconnectMIDIIn", prDisconnectMIDIIn, 3, 0);
	definePrimitive(base, index++, "_DisposeMIDIClient", prDisposeMIDIClient, 1, 0);
	definePrimitive(base, index++, "_RestartMIDI", prRestartMIDI, 1, 0);
        
    definePrimitive(base, index++, "_SendMIDIOut", prSendMIDIOut, 9, 0);

    if (gMIDIClient) midiCleanUp();
}