home *** CD-ROM | disk | FTP | other *** search
- /* MIDIwatcher v1.1, copyright ©1993 by Ed Mackey. */
- /* Source and binary are freely distributable in unmodified form. */
-
- /*
- Launch programs by playing tunes!
-
- Based on "hr" from midi.library, and requires midi.library.
-
- Shows proper method of waiting for, receiving, processing, and
- disposing of MidiPackets.
- */
-
- #include <stdio.h>
- #include <libraries/dos.h>
- #include <exec/lists.h>
- #include <clib/macros.h>
- #include <midi/midi.h>
- #include <functions.h>
- #include <rexx/rxslib.h>
-
- struct library *RexxBase = NULL;
- struct MsgPort *myport = NULL, *RexxPort = NULL;
- struct RexxMsg *mymsg = NULL;
- struct RexxArg *myarg = NULL;
-
- void *MidiBase;
-
- struct MDest *dest;
- struct MRoute *route;
- struct MidiPacket *packet; /* buffer this in case we get shut down before freeing the current message */
-
- long record,tunes,debugON,OutThereMsgs,LineLen;
- FILE *myfile;
- char *sname = "MidiIn";
- char *fname = "S:midiwatch";
-
- #define MAXTUNES 50
- #define MAXCHARS 200
-
- struct NFAnode
- {
- UWORD note;
- WORD failnum;
- struct NFAnode *next;
- } starters[MAXTUNES];
-
- struct program
- {
- char *port;
- char *msg;
- char *cmd;
- } progs[MAXTUNES], failprogs[MAXTUNES];
-
- char tmp[MAXCHARS+2];
-
- struct List InTunes;
- struct InTune
- {
- struct Node node;
- struct NFAnode *note;
- WORD failnum;
- WORD tunenum; /* just for "-d" debug purposes */
- long pos; /* likewise */
- };
-
- _abort(){} /* abort routine called when CTRL-C is hit (Aztec) */
-
- /* The next routine is run if MidiWatcher tries to exit when there are
- * ARexx messages that have been sent but not returned.
- */
-
- void WaitForOuts()
- {
- ULONG flags = (1L << myport->mp_SigBit);
-
- printf("Waiting for ARexx messages to return...\n");
- while (OutThereMsgs > 0) {
- while (mymsg = (void *)GetMsg(myport)) /* assignment intended */
- {
- ClearRexxMsg(mymsg,1);
- DeleteRexxMsg(mymsg);
- OutThereMsgs--;
- }
- if (OutThereMsgs > 0) Wait(flags);
- }
- mymsg = NULL;
- printf("Ah, all done.\n");
- }
-
- void cleanup(int exc)
- {
- if (mymsg) DeleteRexxMsg(mymsg);
- if (packet) FreeMidiPacket (packet);
- if (route) DeleteMRoute (route);
- if (dest) DeleteMDest (dest);
- if (MidiBase) CloseLibrary (MidiBase);
-
- if (OutThereMsgs > 0) WaitForOuts();
-
- if (myport) DeletePort(myport);
- if (RexxPort)
- {
- Forbid();
- while (mymsg = (void *)GetMsg(RexxPort)) /* Assignment intended */
- {
- mymsg -> rm_Result1 = mymsg -> rm_Result2 = 0;
- ReplyMsg((void *)mymsg);
- }
- DeletePort(RexxPort);
- Permit();
- }
- if (RexxBase) CloseLibrary (RexxBase);
- if (myfile) fclose(myfile);
-
- exit(exc);
- }
-
- void die(int exc, char *msg)
- {
- if (msg) printf("%s\n",msg);
- cleanup(exc);
- }
-
- #if 0
- dump (s,len) /* print len bytes in hex */
- UBYTE *s;
- int len;
- {
- while (len--) printf ("%02x ",*s++);
- }
- #endif
-
- /* The next routine takes action when a tune is recognized, or when there
- * is a failure past the "point of no return".
- */
-
- void ExeProg(UWORD prognum, int FailYesNo)
- {
- struct program *tp;
- struct MsgPort *mp;
-
- if (FailYesNo)
- tp = failprogs + prognum;
- else
- tp = progs + prognum;
- if (debugON)
- {
- if (FailYesNo)
- printf("Running failure program %d\n",prognum);
- else
- printf("Running program %d\n",prognum);
- if (tp -> port) printf("ARexx port : %s\n",tp -> port);
- if (tp -> msg) printf("ARexx msg : %s\n",tp -> msg);
- if (tp -> cmd) printf("CLI command: %s\n\n",tp -> cmd);
- }
- if ((tp -> port) && (tp -> msg))
- {
- if (!RexxBase)
- printf("I need " RXSNAME " for ARexx messages!\n");
- else
- {
- mymsg = CreateRexxMsg(myport,NULL,tp -> port);
- if (!mymsg) die(10,"Out of mem!");
- mymsg -> rm_Args[0] = (void *)(tp -> msg);
- if (!FillRexxMsg(mymsg,1,0)) die(10,"Out of mem");
- Forbid();
- if (mp = FindPort(tp -> port)) /* assignment intended */
- {
- PutMsg(mp,(void *)mymsg);
- Permit();
- OutThereMsgs++;
- } else {
- Permit();
- ClearRexxMsg(mymsg,1);
- DeleteRexxMsg(mymsg);
- }
- mymsg = NULL;
- }
- }
- if (tp -> cmd) Execute((tp -> cmd),0,0);
- }
-
- /* The next routine handles incoming MIDI messages. */
-
- int dopak (UBYTE *s, int len) /* Message pointer, Message length */
- {
- struct InTune *tp, *t2;
- struct NFAnode *np, *n2;
- int t;
-
- if (len < 2) return(0); /* Too short? Ignore it. */
- if ((s[0] < 0x90) || (s[0] > 0x9f)) return(0); /* Not a NoteDn? Ignore it! */
- if (record) /* RECORD MODE */
- {
- if ((LineLen += 3) > 75)
- {
- fprintf(myfile,"%02x\n ",s[1]);
- LineLen = 0;
- } else {
- fprintf(myfile,"%02x ",s[1]);
- }
- } else { /* PLAYBACK MODE */
- tp = (void *)InTunes.lh_Head;
- while (t2 = (void *)(tp -> node.ln_Succ)) /* Assignment intended! */
- {
- ++(tp -> pos);
- np = tp -> note;
- n2 = NULL;
- if (s[1] == (np -> note)) n2 = np -> next;
- tp -> note = n2;
- if (n2)
- {
- if (debugON) printf("Tune %d:%d continues. Matched note %02x.\n",
- tp -> tunenum, tp -> pos, s[1]);
- if ((n2 -> failnum) >= 0)
- {
- tp -> failnum = n2 -> failnum;
- if (debugON) printf("Set failure of %d:%d to %d.\n",
- tp -> tunenum, tp -> pos, tp -> failnum);
- }
- if ((n2 -> note) > 255)
- {
- if (debugON) printf("Tune %d completed!\n",
- tp -> tunenum);
- tp -> failnum = -1;
- ExeProg((n2 -> note) - 256, 0);
- }
- } else {
- if (debugON && ((np -> note) < 0x100))
- printf("Tune %d:%d failed. Expecting note %02x, got note %02x.\n",
- tp -> tunenum, tp -> pos, np -> note, s[1]);
- if (tp -> failnum >= 0) ExeProg(tp -> failnum, 1);
- Remove((void *)tp);
- free((void *)tp);
- }
- tp = t2;
- }
- np = starters;
- for (t = 0; t < tunes; t++)
- {
- /* This one just got too messy. */
- // if (debugON) printf("Checking start note %04x, got note %04x\n",np -> note, s[1]);
- if ((np -> note) == s[1])
- {
- if (debugON) printf("Started tune %d\n",t);
- if (!(tp = (void *)malloc(sizeof(struct InTune)))) die(10,"Mem!!");
- tp -> tunenum = t;
- tp -> pos = 1;
- n2 = tp -> note = np -> next;
- tp -> failnum = n2 -> failnum;
- if ((n2 -> note) > 255)
- {
- ExeProg((n2 -> note) - 256, 0);
- free((void *)tp);
- } else {
- AddTail(&InTunes,(void *)tp);
- }
- }
- np++;
- }
- }
- }
-
- dumppacket (packet)
- struct MidiPacket *packet;
- {
- if (packet->Type == MMF_SYSEX) { /* if it's a System Exclusive message... */
- // dump (packet->MidiMsg,MIN(packet->Length,8)); /* only print the first 8 bytes */
- // printf ("... (%d bytes)\n",packet->Length);
- ; /* or just ignore it ;-) */
- }
- else { /* otherwise */
- dopak (packet->MidiMsg,packet->Length); /* process the message */
- }
- }
-
- void Suspend()
- {
- ULONG flags = SIGBREAKF_CTRL_C | (1L << myport->mp_SigBit) | (1L << RexxPort->mp_SigBit);
- int KGS = 0;
-
- if (route) DeleteMRoute (route);
- route = NULL;
- if (dest) DeleteMDest (dest);
- dest = NULL;
-
- while ((!KGS) && (!(Wait(flags) & SIGBREAKF_CTRL_C))) {
- while (mymsg = (void *)GetMsg(myport)) /* assignment intended */
- {
- ClearRexxMsg(mymsg,1);
- DeleteRexxMsg(mymsg);
- OutThereMsgs--;
- }
- while (mymsg = (void *)GetMsg(RexxPort)) /* Assignment intended */
- {
- tmp[0] = 0;
- if (strlen((void *)(mymsg -> rm_Args[0])) < MAXCHARS)
- strcpy(tmp,(void *)(mymsg -> rm_Args[0]));
- mymsg -> rm_Result1 = mymsg -> rm_Result2 = 0;
- ReplyMsg((void *)mymsg);
- mymsg = NULL;
- if (!strcmp(tmp,"QUIT")) die(0,NULL);
- if (!strcmp(tmp,"RESUME")) KGS = 1;
- }
- }
- if (!KGS) die(0,NULL); /* CTRL-C check */
-
- /* create our dest node (private) */
- if (!(dest = CreateMDest (NULL,NULL))) {
- printf ("can't create Dest\n");
- cleanup(10);
- }
- /* create our route to MidiIn or whatever the user specified */
- if (!(route = MRouteDest (sname, dest, NULL))) { /* use default route on our MDest (defaults to everything) */
- printf ("can't create Route (can't find source \"%s\"?)\n",sname);
- cleanup(10);
- }
- }
-
- process ()
- {
- ULONG flags = SIGBREAKF_CTRL_C | (1L << dest->DestPort->mp_SigBit) | (1L << myport->mp_SigBit);
-
- if (RexxPort) flags |= (1L << RexxPort->mp_SigBit);
- while (!(Wait(flags) & SIGBREAKF_CTRL_C)) { /* wait until we get a message or CTRL-C is hit, quit on CTRL-C */
- while (packet = GetMidiPacket (dest)) { /* get a packet */
- dumppacket (packet); /* print it */
- FreeMidiPacket (packet); /* free it */
- }
- while (mymsg = (void *)GetMsg(myport)) /* assignment intended */
- {
- ClearRexxMsg(mymsg,1);
- DeleteRexxMsg(mymsg);
- OutThereMsgs--;
- }
- if (RexxPort) {
- while (mymsg = (void *)GetMsg(RexxPort)) /* Assignment intended */
- {
- tmp[0] = 0;
- if (strlen((void *)(mymsg -> rm_Args[0])) < MAXCHARS)
- strcpy(tmp,(void *)(mymsg -> rm_Args[0]));
- mymsg -> rm_Result1 = mymsg -> rm_Result2 = 0;
- ReplyMsg((void *)mymsg);
- mymsg = NULL;
- if (!strcmp(tmp,"QUIT")) return();
- if (!strcmp(tmp,"SUSPEND"))
- {
- Suspend();
- flags = SIGBREAKF_CTRL_C |
- (1L << dest->DestPort->mp_SigBit) |
- (1L << myport->mp_SigBit) |
- (1L << RexxPort->mp_SigBit);
- }
- }
- }
- }
- }
-
- /* The next routine makes an NFA (Nondeterministic Finite-state Automaton) out
- * of the user's tunes. I learned about them in CSc 318 at Lehigh, with
- * professor Ed Kay. I always meant to do an NFA -> DFA conversion, so as to
- * take less CPU time during the actual realtime processing of incoming notes.
- * I learned the conversion in 318 also, but I never had the time or energy to
- * implement it in the context of this silly program.
- *
- * Feel free to try it yourself.
- * If you succeed, you _must_ send me a copy.
- */
-
- void MakeNFA()
- {
- long mode,ch,hex;
- char *tmp2, *tp;
- struct NFAnode *n1, *n2;
-
- mode = -1L;
- while ((ch = fgetc(myfile)) != EOF)
- {
- if ((!mode) && (((ch >= '0') && (ch <= '9')) || ((ch >= 'a') && (ch <= 'f'))))
- {
- tmp[0] = ch; tmp[1] = fgetc(myfile); tmp[2] = 0;
- sscanf(tmp,"%lx",&hex);
- n2 = (void *)malloc(sizeof(struct NFAnode));
- if (!n2) die(10,"Out of mem");
- n2 -> failnum = -1;
- n1 -> note = hex;
- n1 -> next = n2;
- n2 -> note = tunes + 256;
- n2 -> next = NULL;
- n1 = n2;
- } else if (mode > 0) {
- if (ch == 10)
- {
- *tp = 0;
- if (tmp[0])
- {
- tmp2 = (void *)malloc(strlen(tmp) + 1);
- if (!tmp2) die(10,"Out of mem");
- strcpy(tmp2,tmp);
- switch(mode)
- {
- case 1: progs[tunes].cmd = tmp2; break;
- case 2: progs[tunes].port = tmp2; break;
- case 3: progs[tunes].msg = tmp2; break;
- case 4: failprogs[tunes].cmd = tmp2; break;
- case 5: failprogs[tunes].port = tmp2; break;
- case 6: failprogs[tunes].msg = tmp2; break;
- }
- }
- mode = -1;
- } else {
- *(tp++) = ch;
- if ((ULONG)tp >= (ULONG)(tmp + MAXCHARS)) tp--;
- }
- } else if (mode == -2) {
- if (ch == 10) mode = -1;
- } else switch(ch) {
- case '$': mode = 0; tmp[0] = 0;
- if ((++tunes) >= MAXTUNES) die(10,"Too many tunes");
- progs[tunes].port = NULL;
- progs[tunes].msg = NULL;
- progs[tunes].cmd = NULL;
- failprogs[tunes].port = NULL;
- failprogs[tunes].msg = NULL;
- failprogs[tunes].cmd = NULL;
- n1 = &(starters[tunes]);
- n1 -> note = 0;
- n1 -> next = NULL;
- n1 -> failnum = -1;
- break;
- case ':': if (!mode) n1 -> failnum = tunes; break;
- case '>': mode = 1; tp = tmp; tmp[0] = 0; break;
- case '@': mode = 2; tp = tmp; tmp[0] = 0; break;
- case '*': mode = 3; tp = tmp; tmp[0] = 0; break;
- case ']': mode = 4; tp = tmp; tmp[0] = 0; break;
- case '+': mode = 5; tp = tmp; tmp[0] = 0; break;
- case '!': mode = 6; tp = tmp; tmp[0] = 0; break;
- case '#': mode = -2; break;
- }
- }
- tunes++;
- }
-
- main(argc,argv)
- char **argv;
- {
- int t;
-
- record = OutThereMsgs = debugON = LineLen = 0;
- myfile = NULL;
- tunes = -1;
-
- if (argc > 1 && *argv[1] == '?') {
- printf("MidiWatcher v1.1 by Ed Mackey. Watch MIDI keyboard for tunes.\n");
- printf("Usage: %s [-i source] [-f savefile] [-r] [-d]\n",argv[0]);
- printf(" -i selects a MidiIn source, defaults to MidiIn\n");
- printf(" -f selects a tune save file, defaults to S:midiwatch\n");
- printf(" -r turns on RECORD mode and adds a tune to file -f\n");
- printf(" -d turns on DEBUG mode (playback only) to see your tunes go.\n");
- exit (1);
- }
-
- if (!(MidiBase = OpenLibrary (MIDINAME,MIDIVERSION))) {
- printf ("can't open midi.library\n");
- cleanup(10);
- }
-
- RexxBase = OpenLibrary(RXSNAME,0L); /* RexxBase may be NULL after this! */
-
- myport = CreatePort(NULL,0);
- if (!myport) die(10,"No mem for replyport");
-
- t = 1;
- while (argc > t)
- {
- if (argv[t][0] == '-')
- {
- switch(argv[t][1])
- {
- case 'i': if (argv[t][2]) sname = &(argv[t][2]);
- else sname = argv[++t];
- break;
- case 'f': if (argv[t][2]) fname = &(argv[t][2]);
- else fname = argv[++t];
- break;
- case 'r': record = 1; break;
- case 'd': debugON = 1; break;
- }
- }
- t++;
- }
- /* create our dest node (private) */
- if (!(dest = CreateMDest (NULL,NULL))) {
- printf ("can't create Dest\n");
- cleanup(10);
- }
- /* create our route to MidiIn or whatever the user specified */
- if (!(route = MRouteDest (sname, dest, NULL))) { /* use default route on our MDest (defaults to everything) */
- printf ("can't create Route (can't find source \"%s\"?)\n",sname);
- cleanup(10);
- }
-
- if (record)
- {
- myfile = fopen(fname,"a");
- printf("Play a tune and press CTRL-C\n");
- } else {
- myfile = fopen(fname,"rb");
- }
-
- if (!myfile)
- {
- printf("Unable to open file %s\n",fname);
- cleanup(10);
- }
-
- NewList(&InTunes);
-
- if (record)
- fprintf(myfile,"# Your comments go here (such as name of tune)...\n"
- "# You may wish to insert a colon (\":\") at the point of no return...\n$");
- else
- {
- MakeNFA();
- if (debugON) printf("Loaded %d tunes\n",tunes);
- if (RexxBase)
- { /* If MidiWatcher is already running, try to kill it. */
- struct MsgPort *mp;
-
- mymsg = CreateRexxMsg(myport,NULL,"MIDIWATCHER");
- if (!mymsg) die(10,"Out of mem!");
- mymsg -> rm_Args[0] = (void *)("QUIT");
- if (!FillRexxMsg(mymsg,1,0)) die(10,"Out of mem");
- Forbid();
- if (mp = FindPort("MIDIWATCHER")) /* assignment intended */
- {
- PutMsg(mp,(void *)mymsg);
- Permit();
- OutThereMsgs++;
- } else {
- Permit();
- ClearRexxMsg(mymsg,1);
- DeleteRexxMsg(mymsg);
- }
- mymsg = NULL;
- }
- RexxPort = CreatePort("MIDIWATCHER",4L);
- if (!RexxPort) die(10,"No mem for port");
- }
-
- process(); /* process until shutdown */
-
- if (record)
- {
- printf("Saving and exiting...\n");
- fprintf(myfile,"\n"
- "#### Success program...\n"
- "@PLAY\n"
- "*id midi_event\n"
- ">echo \"Tune recognized\"\n"
- "#### Failure program...\n"
- "+PLAY\n"
- "!id midi_fail\n"
- "]echo \"You passed the colon, and then blew it!!\"\n\n");
- }
- cleanup(0);
- }
-
- /* End of midiwatcher.c */
-