/* A simple layer upon SGI midi by Pieter Suurmond, june 1, 2004. Include this file in the makefile for ComParser MIDI on SGI IRIX. */ #include #include #include /* IRIX specific. */ #include #include /* Poll() is like select(). */ #include #include "CMP_midi.h" /* Comparser include from parent directory. */ /*---------------------------------------------------- SGI Driver Internals: -------------*/ typedef struct MD_SPECIFIC_SGI /* Ptr to this data will be stored in our encap- */ { /* sulating, more generic CMP_MIDI_IO struct. It */ MDport inport, /* will however only be accessed in THIS file. */ outport; /* Port connected to named interface. */ } MD_SPECIFIC_SGI; /* For fun, compare this with the huge 'MD_SPECIFIC_OMS'-struct. */ /*----------------------------------------------------------------------------------------*/ void listNamesMIDI(CMP_MIDI_IO* mio, FILE* dest) /* Prints names that may be used in se- */ { /* lection of input and output ports. */ int i; char* iface; mio->numInterfaces = mdInit(); /* No harm if done multiple times (keep up to date). */ fprintf(dest, "\n%d MIDI interfaces available:\n", mio->numInterfaces); for (i = 0; i < mio->numInterfaces; i++) { iface = mdGetName(i); if (iface) fprintf(dest, " %d: %s\n", i, iface); } } /*----------------------------------------------------------------------------------------*/ void* specificOpenMIDI(CMP_MIDI_IO* mio) /* Sign on to IRIX midi. */ { short n; MD_SPECIFIC_SGI* result = (MD_SPECIFIC_SGI*)NULL; if (!mio) return ((void*)result); mio->numInterfaces = mdInit(); /* First, we initialize the MIDI library. */ if (mio->numInterfaces == 0) { sprintf(mio->rep255, "Error: no SGI MIDI interfaces found.\n"); return ((void*)result); } else n = sprintf(mio->rep255, "%d SGI MIDI interfaces found.\n", mio->numInterfaces); result = (MD_SPECIFIC_SGI*)malloc(sizeof(MD_SPECIFIC_SGI)); if (!result) { sprintf(mio->rep255, "Error: not enough memory for SGI MIDI.\n"); return ((void*)result); } /*-------------------------------- Make output connection: --------------*/ result->outport = mdOpenOutPort(mio->outName); /* Arg may be NULL. */ if (!result->outport) /* Cannot open MIDI interface for output. */ { /* Release (opponent of mdInit() not needed?) */ sprintf(mio->rep255, "Error: SGI mdOpenOutPort(%s) = NULL.\n", mio->outName); goto freeExit; } else if (mio->outName) n += sprintf(mio->rep255 + n, " MIDI output connected to '%s'.\n", mio->outName); else n += sprintf(mio->rep255 + n, " Default MIDI output connection.\n"); /*-------------------------------- Make input connection: ---------------*/ result->inport = mdOpenInPort(mio->inName); /* Arg may be NULL. */ if (!result->inport) /* Cannot open MIDI interface for input. */ { sprintf(mio->rep255, "Error: SGI mdOpenInPort(%s) = NULL.\n", mio->inName); mdClosePort(result->outport); /* On ERROR, close succesfully opened output. */ goto freeExit; } else if (mio->inName) n += sprintf(mio->rep255 + n, " MIDI input connected to '%s'.\n", mio->inName); else n += sprintf(mio->rep255 + n, " Default MIDI input connection.\n"); /*-----------------------------------------------------------------------*/ mdSetStampMode(result->outport, MD_NOSTAMP); /* Use 'NOSTAMP'-mode for outputs for now. */ goto okExit; freeExit: free(result); /* Sure it's there. */ result = (MD_SPECIFIC_SGI*)NULL; okExit: return ((void*)result); } /*-------------------------------------------------------------*/ /* cmpCloseMIDI -- Close up shop and sign out from SGI MIDI. */ void specificCloseMIDI(CMP_MIDI_IO* mio) { /* Arg MUST be there. */ MD_SPECIFIC_SGI* sgiMIDI = (MD_SPECIFIC_SGI*)mio->specific; /* Unfortunately, closing a port will cause any pending MIDI * data to be discarded. Therefore, we need to wait until * all of the events actually go out. To do this, we call * the function mdTellNow() which tells us what tick is currently * being transmitted. In order to remain a good Unix citizen, * we sleep in between tests so that we don't unnecessarily * consume the CPU. */ if (sgiMIDI) { /* while (mdTellNow(sgiMIDI->outport) < stamp) sleep(1); */ mdClosePort(sgiMIDI->outport); /* Assume both were still open. */ mdClosePort(sgiMIDI->inport); sprintf(mio->rep255, "Logged out from SGI MIDI.\n"); free(sgiMIDI); mio->specific = (void*)NULL; } else sprintf(mio->rep255, "Failed to close down SGI MIDI ports!\n"); } /* typedef struct __mdevent { char msg[4]; // channel message data char *sysexmsg; // sysex message data unsigned long long stamp; // time stamp in nanosecs int msglen; // length of data, sysex only } MDevent; */ /*-----------------------------------------------------------------------------*/ /* Send a MIDI message out the output port. Returns 0 on succes, negative on failure (then writes error report in struct, if it's there: mio->rep255). FIX: Argument 'interface' is not used! */ int sendMIDI(CMP_MIDI_IO* mio, unsigned char* ptr, int length, int interface) { int i; MDevent event; /* IRIX MIDI event structure. */ MD_SPECIFIC_SGI* sgiMIDI; (void)interface; /* Surpress unused warning. */ if (!mio) return(-1024); /* No storage for any message. */ sgiMIDI = (MD_SPECIFIC_SGI*)(mio->specific); if (!sgiMIDI) { sprintf(mio->rep255, "Missing mio->specific in SGI sendMIDI().\n"); return(-1025); } if (*ptr == 0xF0) /* In case of sysex, IRIX wants the int-field */ /* 'msglen' to contain length and char-pointer */ { /* to the data, which last byte MUST be F7. */ event.sysexmsg = (char*)ptr; event.msglen = length; if (ptr[length - 1] != 0xF7) { sprintf(mio->rep255, "Missing sysex-F7 in SGI sendMIDI().\n"); return (-1026); } } else /* Other messages have defined (MIDI-protocol) */ { /* lengths (no running status here) and must */ for (i=0; ioutport, &event, 1)) /* Send just 1 single event. */ return (0); sprintf(mio->rep255, "SGI : sendMIDI() : mdSend() failed.\n"); return (-1027); } /*--------------------------------------------------------------------*/ /* Within a real time environment call this routine regularly to poll */ /* for newly arrived MIDI data. It will call the funtions you provide */ /* for elsewhere. It resets an overflow status after emptying buffer. */ /* Returns number of messages processed (>=0) or negative on failure. */ long receiveMIDI(CMP_MIDI_IO* mio) { char* funNam = "sgi_midi.c : receiveMIDI()"; long processed = 0L; /* Negative return value on errors. */ unsigned char channel; int bend; MD_SPECIFIC_SGI* sgiMIDI; /* Comparser struct. */ struct pollfd pfd; /* SGI Poll structure for MIDI in. */ int nfds; /* Number of ready file descriptors */ MDevent mdv; /* MIDI event being received/forwarded */ if (!mio) return(-1L); sgiMIDI = (MD_SPECIFIC_SGI*)(mio->specific); if (!sgiMIDI) { sprintf(mio->rep255, "Missing specific data in %s.\n", funNam); return(-2L); } pfd.fd = mdGetFd(sgiMIDI->inport); /* Poll for input on the file descriptor. */ pfd.events = POLLIN; while (1 == (nfds = poll(&pfd, 1, 0))) /* Last arg is timeout, 0 ret immediately, */ { /* -1 would cause blocking. */ if (!(pfd.revents & POLLIN)) /* Be VERY sure it is no other weird condition. */ { sprintf(mio->rep255, "Some other condition polled in %s.\n", funNam); return(-3L); } if (mdReceive(sgiMIDI->inport, &mdv, 1) < 0) { sprintf(mio->rep255, "In %s, mdReceive() failed.\n", funNam); return(-4L); } /*------------------------------------ SOFTWARE THRU: -------------------*/ if (mio->recThru) { if (mdSend(sgiMIDI->outport, &mdv, 1) < 0) { sprintf(mio->rep255, "In %s, THRU failed.\n", funNam); return(-5L); /* What if it were allocated sysex!? */ } } /*-------------------------------- PROCESS EVENTS: --------------------------*/ if (mdv.msg[0] == 0xF0) /* Was allocated (real time) by the system. */ { /* Only sysex if manufactorer ID matches. */ if (mdv.msg[1] == mio->recSysexID) { recMidiSystemExclusive((unsigned char*)&mdv.sysexmsg, mdv.msglen); processed++; } mdFree(mdv.sysexmsg); /* But it must be released anyway. */ goto prceed; } if ((mdv.msg[0] & 0xF0) == 0xF0) /* Other RT-messages not implemented. */ { sprintf(mio->rep255, "In %s non-sysex F-messages not yet supported.\n", funNam); return(-6L); } channel = mdv.msg[0] & 0x0F; if ((!mio->recOmni) && (mio->recChan != channel)) /* Selected or OMNI? */ goto prceed; switch(mdv.msg[0] & 0xF0) /* SURE NO RUNNING STATUS HERE ANYMORE: */ { case 0x80: recMidiNoteOff (channel, mdv.msg[1], mdv.msg[2]); break; case 0x90: recMidiNoteOn (channel, mdv.msg[1], mdv.msg[2]); break; case 0xA0: recMidiPolyPressure (channel, mdv.msg[1], mdv.msg[2]); break; case 0xB0: recMidiController (channel, mdv.msg[1], mdv.msg[2]); break; case 0xC0: recMidiProgramChange (channel, mdv.msg[1]); break; case 0xD0: recMidiChannelPressure(channel, mdv.msg[1]); break; case 0xE0: bend = (int)mdv.msg[1] + (((int)mdv.msg[2])<< 7); /* LSB,MSB. */ recMidiPitchBend (channel, bend); break; default: sprintf(mio->rep255,"Statusbyte (%d) < 128 in %s.\n", (int)mdv.msg[0], funNam); return(-7L); } processed++; /* Count number of processed messages. */ prceed: channel = channel; /* Dummy instruction to jump to. */ } if (nfds) /* Not 0 and not 1, thus negative or >1. */ { sprintf(mio->rep255, "In %s, poll() failed (nfds=%d).\n", funNam, nfds); return(-8L); } /* mio->recOverflow = 0; Forget this state. */ return processed; }