[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();
}