home *** CD-ROM | disk | FTP | other *** search
/ Chestnut's Multimedia Mania / MM_MANIA.ISO / midi / cmtcmu / moxc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-28  |  15.4 KB  |  516 lines

  1. /* MOXC -- a C version of Collinge's MOXIE language    */
  2.  
  3. /*****************************************************************************
  4. *    Change Log
  5. *  Date        | Change
  6. *-----------+-----------------------------------------------------------------
  7. * 31-Dec-85 | Modified for use with midi
  8. *  5-Feb-86 | Added m_rest and m_restuntil allowing rests at top level
  9. * 28-May-86 | Added command line parsing
  10. *  4-Jun-86 | changed keyevent to separate calls for each event type
  11. * 10-Jul-86 | put loop in mainscore with prompt to play and replay
  12. *****************************************************************************/
  13.  
  14. /* IMPORTS:
  15.     asciievent(k)        user-defined action for terminal input
  16.     bendchange(ch, val)    user-defined pitch bend handler
  17.     ctrlchange(ch, c, val)    user-defined control change handler
  18.     keydown(ch, p, v)    user-defined MIDI note on handler
  19.     keyup(ch, p)        user-defined MIDI note off handler
  20.     mainscore()        user-defined first action(s)
  21.     musicfns        lots of time and io functions
  22.     peddown(ch)        user-defined pedal down handler
  23.     pedup(ch)        user-defined pedal up handler
  24.     touchchange(ch, val)    user-defined aftertouch handler
  25. */
  26. asciievent();
  27. bendchange();
  28. ctrlchange();
  29. keydown();
  30. keyup();
  31. mainscore();
  32. peddown();
  33. pedup();
  34. prgmchange();
  35. touchchange();
  36.  
  37. /* EXPORTS:
  38.     cause(delay, routine, p1, p2, ..., p8)
  39.     moxcdone -- set to true to quit
  40.     eventtime -- ideallized current time
  41. */
  42.  
  43. #include "cext.h"
  44. #include "stdio.h"
  45. #include "malloc.h"
  46. #include "mpu.h"
  47. #include "cmdline.h"
  48. #include "midicode.h"
  49. #include "moxc.h"
  50.  
  51. #define SAFEMOXC true
  52.  
  53. /* lists of switches and options */
  54. #define nswitches 8
  55. private char *switches[nswitches] = 
  56.     { "-debug", "-d", "-miditrace", "-m", "-trace", "-t", "-help", "-block" };
  57. #define noptions 1
  58. private char *options[1] = { "-tune" };
  59.  
  60. #define n_d_sw 2
  61. private char *d_switches[n_d_sw] = { "-d", "-debug" };
  62.  
  63.  
  64. /* number of events to prepare to run on each main loop */
  65. #define NPREPARE 5
  66.  
  67. #define NPRIORITY 10
  68.  
  69. typedef struct event {
  70.     struct event *next; /* link to next event record */
  71.     long time;        /* time of this event */
  72.     int priority;    /* priority of this event (0 is highest) */
  73.     int (*routine)();    /* who to call */
  74.     int p1, p2, p3, p4, p5, p6, p7, p8; /* what to pass */
  75. } *event_type;
  76.  
  77. /*
  78.  * pending is used to sort events by priority and hold them until
  79.  * there is time to insert them in the sorted evqueue.  Note that
  80.  * in this implementation, only one priority is used.
  81.  * nodes are allocated/returned from/to evfree
  82.  */
  83. event_type pending[NPRIORITY];
  84. int npending;    /* tells how many events are in pending array */
  85. event_type evqueue;        /* waiting to run event queue */
  86. event_type evfree = NULL;    /* free list */
  87. int moxcdone;    /* flag to halt execution */
  88. long eventtime; /* time of current event -- used to avoid timing errors due */
  89.         /*  to finite execution speed */
  90. int debug = false;
  91.  
  92. /*****************************************************************************
  93. *    Routines local to this module
  94. *****************************************************************************/
  95. private void        cmdline_help();
  96. private event_type    evallocate();
  97. private event_type    evcdr();
  98. private event_type    evcons();
  99. private    void        evdeallocate();
  100. private boolean        evgtr();
  101. private event_type    evinsert();
  102. private    void        evrun();
  103. private    void        evshow();
  104. private    void        moxcinit();
  105. private    void        moxcpoll();
  106. private    void        schedule();
  107.  
  108. /****************************************************************************
  109. *                cause
  110. * Inputs:
  111. *    int delay: time before this event should occur
  112. *    int (*routine)(): routine that implements the event
  113. *    int p1 through p8: parameters to pass to routine
  114. * Effect: 
  115. *    builds an event and puts it in pending queue for later scheduling
  116. ****************************************************************************/
  117.  
  118. void cause(delay, routine, p1, p2, p3, p4, p5, p6, p7, p8)
  119.     int delay, (*routine)(), p1, p2, p3, p4, p5, p6, p7, p8;
  120. {
  121.     event_type ev;
  122.     int priority;
  123. /*    priorities are not currently implemented:
  124.     if (priority >= NPRIORITY) priority = NPRIORITY-1;
  125.     if (priority <= 0) priority = 0;
  126. */
  127.     priority = 0;
  128. #ifdef SAFEMOXC
  129.     if (routine == 0) {
  130.         printf("Error: cause called with NULL routine\n");
  131.     musicterm();
  132.     exit(1);
  133.     }
  134. #endif
  135.     ev = evallocate();
  136.     ev->time = eventtime + delay;
  137.     ev->priority = priority;
  138.     ev->routine = routine;
  139.     ev->p1 = p1;
  140.     ev->p2 = p2;
  141.     ev->p3 = p3;
  142.     ev->p4 = p4;
  143.     ev->p5 = p5;
  144.     ev->p6 = p6;
  145.     ev->p7 = p7;
  146.     ev->p8 = p8;
  147.  
  148.     npending++;
  149.     pending[priority] = evcons(ev, pending[priority]);
  150.     if (debug) {
  151.     printf("(cause) event is pending:");
  152.     evshow(ev);
  153.     }
  154. }
  155.  
  156. /****************************************************************************
  157. *                 cmdline_help
  158. * Effect: 
  159. *    Prints out command line help
  160. ****************************************************************************/
  161.  
  162. private void cmdline_help()
  163. {
  164.     fprintf(stderr,"program_name [options]\n");
  165.     fprintf(stderr,"     Options are below.  Those with * are for wizards:\n");
  166.     fprintf(stderr,"       -block         disable MIDI thru\n");
  167.     fprintf(stderr,"       -debug (-d)         enable verbose debug mode\n");
  168.     fprintf(stderr,"       -help         this message\n");
  169.     fprintf(stderr,"       -miditrace (-m)   turn on MIDI command trace\n");
  170.     fprintf(stderr,"       -tune file         use tuning from file\n");
  171.     fprintf(stderr,"       -trace (-t)         trace music\n");
  172. }
  173.  
  174. /****************************************************************************
  175. *                evallocate
  176. * Outputs:
  177. *    returns event_type allocated from freelist or with malloc
  178. ****************************************************************************/
  179.  
  180. private event_type evallocate()
  181. {
  182.     if (evfree) return evcdr(&evfree);
  183.     /* else */ return ((event_type) malloc(sizeof(struct event)));
  184. }
  185.  
  186. /****************************************************************************
  187. *                evcdr
  188. * Inputs:
  189. *    event_type *evlist: address of a list pointer
  190. * Outputs:
  191. *    returns the head of the list
  192. * Effect:
  193. *    removes the head of the list from *evlist
  194. ****************************************************************************/
  195.  
  196. private event_type evcdr(evlist)
  197.     event_type *evlist;
  198. {
  199.     event_type ptr;
  200.     ptr = *evlist;
  201.     *evlist = (*evlist)->next;
  202.     ptr->next = NULL;
  203.     return ptr;
  204. }
  205.  
  206. /****************************************************************************
  207. *                evcons
  208. * Inputs:
  209. *    event_type ev: event to put at the head of the list
  210. *    event_type evlist: the list
  211. * Outputs:
  212. *    returns list with ev at the head
  213. * Effect: modifies ev's next pointer
  214. ****************************************************************************/
  215.  
  216. private event_type evcons(ev, evlist)
  217.     event_type evlist, ev;
  218. {
  219.     ev->next = evlist;
  220.     return ev;
  221. }
  222.  
  223. /****************************************************************************
  224. *                evdeallocate
  225. * Inputs:
  226. *    event_type ev: a node to deallocate
  227. * Effect: 
  228. *    returns ev to free list
  229. ****************************************************************************/
  230.  
  231. private void evdeallocate(ev)
  232.     event_type ev;
  233. {
  234.     evfree = evcons(ev, evfree);
  235. }
  236.  
  237. /****************************************************************************
  238. *                evgtr
  239. * Inputs:
  240. *    event_type ev1, ev2: two events to be compared
  241. * Outputs:
  242. **    returns true if ev1 is earlier or has lower priority number than ev2
  243. ****************************************************************************/
  244.  
  245. private boolean evgtr(ev1, ev2)
  246.     event_type ev1, ev2;
  247. {
  248.     return (ev1->time < ev2->time) ||
  249.        ((ev1->time == ev2->time) && (ev1->priority < ev2->priority));
  250. }
  251.  
  252. /****************************************************************************
  253. *                evinsert
  254. * Inputs:
  255. *    event_type evlist: the list (a priority queue)
  256. *    event_type ev: the event to insert in evlist
  257. * Outputs:
  258. *    returns list resulting from inserting ev into evlist
  259. * Implementation:
  260. *    if ev goes at the head, ev is cons onto evlist and returned
  261. *    otherwise evlist is modified by inserting ev at the right spot
  262. ****************************************************************************/
  263.  
  264. private event_type evinsert(evlist, ev)
  265.     event_type evlist, ev;
  266. {
  267.     event_type ptr;
  268.     if (!evlist) {    /* no list, return event */
  269.     ev->next = NULL;
  270.     return ev;
  271.     }
  272.     if (evgtr(ev, evlist)) { /* make event first on list */
  273.     return evcons(ev, evlist);
  274.     }
  275.     ptr = evlist;
  276.     while ((ptr->next) && evgtr(ptr->next, ev)) {
  277.     ptr = ptr->next;
  278.     }
  279.     ev->next = ptr->next;
  280.     ptr->next = ev;
  281.     return evlist;
  282. }
  283.  
  284. /****************************************************************************
  285. *                evrun
  286. * Inputs:
  287. *    event_type ev: the event to execute
  288. * Effect: 
  289. *    executes the previously scheduled event ev and deallocates it
  290. ****************************************************************************/
  291.  
  292. private void evrun()
  293. {
  294.     event_type ev;
  295.     if (debug) {
  296.     printf("(evrun) running an event: \n");
  297.     }
  298.     ev = evcdr(&evqueue);
  299.     eventtime = ev->time;
  300.     if (debug) evshow(ev);
  301.     (*(ev->routine))
  302.     (ev->p1, ev->p2, ev->p3, ev->p4, ev->p5, ev->p6, ev->p7, ev->p8);
  303.     evdeallocate(ev);
  304. }
  305.  
  306. /****************************************************************************
  307. *                evshow
  308. * Inputs:
  309. *    eventtype ev: the event to show
  310. * Effect: 
  311. *    prints a description of ev
  312. * Assumes:
  313. *    ev is not null
  314. ****************************************************************************/
  315. private void evshow(ev)
  316.     event_type ev;
  317. {
  318.     printf("address:  %d\n", ev);
  319.     printf("time:     %ld\n", ev->time);
  320.     printf("priority: %d\n", ev->priority);
  321.     printf("routine:  %d\n", ev->routine);
  322.     printf("parameters: %d, %d, %d, %d, %d, %d, %d, %d\n",
  323.      ev->p1, ev->p2, ev->p3, ev->p4, ev->p5, ev->p6, ev->p7, ev->p8);
  324. }
  325.  
  326. /****************************************************************************
  327. *                    m_rest
  328. * Inputs:
  329. *    int time: Amount of time to rest
  330. * Effect: 
  331. *    Waits until the amount of time specified has lapsed
  332. * Assumes:
  333. *    Must not be called from a "caused" routine.  Must only be called
  334. *    from "mainscore" or a routine called directly or indirectly from
  335. *    "mainscore" without using "cause".
  336. ****************************************************************************/
  337.  
  338. void m_rest(time)
  339.     int time;
  340. {
  341.     m_restuntil(time + gettime());    
  342. }
  343.  
  344. /****************************************************************************
  345. *                  m_restuntil
  346. * Inputs:
  347. *    int time: Event time to rest until
  348. * Effect: 
  349. *    Waits until the specified time has been reached (absolute time).
  350. *    Other "caused" events will take place during the rest provided
  351. *    this routine is called from "mainscore" (see m_rest description).
  352. ****************************************************************************/
  353. void m_restuntil(time)
  354.     int time;
  355. {
  356.     while(time > gettime()) {
  357.     moxcpoll();
  358.     }
  359. }
  360.  
  361. /****************************************************************************
  362. *                main
  363. * Inputs:
  364. *    int argc: number of command line arguments
  365. *    char * argv: command line argument array
  366. * Effect: 
  367. *    initializes and runs moxc program
  368. ****************************************************************************/
  369.  
  370. void main(argc,argv)
  371.     int argc;
  372.     char * argv[];
  373. {
  374.     while (askbool("Type RETURN to play, or N RETURN to quit", true)) {
  375.     moxcinit(argc, argv);    /* initialize structures */
  376.     mainscore();        /* call user's start program */
  377.     while (!moxcdone) {        /* test for finish */
  378.         if (!evqueue) moxcdone |= (npending == 0);
  379.         moxcpoll();        /* do work */
  380.     }
  381.     musicterm();
  382.     printf("End of Moxc execution.\n");
  383.     }
  384. }
  385.  
  386. /****************************************************************************
  387. *                moxcinit
  388. * Inputs:
  389. *    int argc: number of command line arguments
  390. *    char * argv: command line argument array
  391. * Effect: initializes moxc system
  392. ****************************************************************************/
  393.  
  394. private void moxcinit(argc, argv)
  395.     int argc;
  396.     char * argv[];
  397. {
  398.     int i;    /* loop variable */
  399.  
  400.     cl_init(switches, nswitches, options, noptions, argv, argc);
  401.  
  402.     if (cl_switch("-help")) {
  403.     cmdline_help(); 
  404.     exit(0);
  405.     }
  406.  
  407.     debug = (cl_nswitch(d_switches, n_d_sw) != NULL);
  408.  
  409.     for (i=0; i < NPRIORITY; i++) pending[i] = NULL;
  410.     evqueue = NULL;
  411.     eventtime = 0;
  412.  
  413.     musicinit();    
  414.  
  415.     moxcdone = 0;
  416. }
  417.  
  418. /****************************************************************************
  419. *                moxcpoll
  420. * Effect: dispatch on user inputs, cause events
  421. ****************************************************************************/
  422.  
  423. private void moxcpoll()
  424. {
  425.     long now;        /* current time */
  426.     int k;        /* terminal input (this is int so users do not
  427.              * have to declare type of parameter */
  428.     byte midi_data[4];    /* midi input */
  429.  
  430.     /* get the time */
  431.     now = gettime();
  432.     /* see if any user-caused events have happened */
  433.     eventtime = now;
  434.     /* poll for and decode midi keyboard input */
  435.         if (getbuf(false, midi_data)) {
  436.         byte code = midi_data[0] & MIDI_CODE_MASK;
  437.         if (code == MIDI_ON_NOTE) {
  438.             if (midi_data[2] == 0) {    /* velocity 0 -> note off */
  439.             keyup((midi_data[0] & MIDI_CHN_MASK) + 1,
  440.                   midi_data[1] - 12);
  441.             } else {
  442.             keydown((midi_data[0] & MIDI_CHN_MASK) + 1,
  443.                 midi_data[1] - 12, midi_data[2]);
  444.             }
  445.         } else if (code == MIDI_OFF_NOTE) {
  446.             keyup((midi_data[0] & MIDI_CHN_MASK) + 1,
  447.                   midi_data[1] - 12);
  448.         } else if (code == MIDI_TOUCH) {
  449.             touchchange((midi_data[0] & MIDI_CHN_MASK) + 1,
  450.                 midi_data[1]);
  451.         } else if (code == MIDI_BEND) {
  452.             bendchange((midi_data[0] & MIDI_CHN_MASK) + 1,
  453.                    midi_data[1] + (midi_data[2] << 7));
  454.         } else if (code == MIDI_CTRL && midi_data[1] == SUSTAIN) {
  455.             if (midi_data[2] == 0)
  456.             pedup((midi_data[0] & MIDI_CHN_MASK) + 1);
  457.             else peddown((midi_data[0] & MIDI_CHN_MASK) + 1);
  458.         } else if (code == MIDI_CTRL) {
  459.             ctrlchange((midi_data[0] & MIDI_CHN_MASK) + 1,
  460.                    midi_data[1], midi_data[2]);
  461.         } else if (code == MIDI_CH_PROGRAM) {
  462.             prgmchange((midi_data[0] & MIDI_CHN_MASK) + 1,
  463.                     midi_data[1]);
  464.         }
  465.         }
  466.     /* poll ASCII keyboard */
  467.         if (kbhit()) {
  468.         k = getch();
  469.         asciievent(k);
  470.         if (debug && evqueue)
  471.             printf("nextevent is scheduled for %ld\n", evqueue->time);
  472.         }
  473.     /* if it is now time, run the next event */
  474.     else if ((evqueue != NULL) && (now >= evqueue->time)) evrun();
  475.     /* if events are waiting to be scheduled ... */
  476.     if (npending > 0) schedule();
  477. }
  478.  
  479. /****************************************************************************
  480. *                quit
  481. * Effect: tells moxc to shut down
  482. ****************************************************************************/
  483.  
  484. void quit()
  485. {
  486.     moxcdone = true;
  487. }
  488.  
  489. /****************************************************************************
  490. *                schedule
  491. * Effect: takes events off pending queues and prepares them to run
  492. ****************************************************************************/
  493.  
  494. private void schedule()
  495. {
  496.     int i, n;
  497.     event_type ev;
  498.  
  499.     /* insert up to NPREPARE events from pending queues */
  500.     i = 0;    /* i is number of events inserted */
  501.     n = 0;    /* n is the priority */
  502.     while (i < NPREPARE && n < NPRIORITY) {
  503.     if (pending[n]) {
  504.         npending--;
  505.         ev = evcdr(&pending[n]);
  506.         evqueue = evinsert(evqueue, ev);
  507.         i++;
  508.         if (debug) {
  509.         printf("(main) event inserted: \n");
  510.         evshow(ev);
  511.         }
  512.     } else {
  513.         n++;
  514.     }
  515.     }        
  516. }