/* phase2.c -- this module plays notes compiled and sorted in phase1 */ /***************************************************************************** * Change Log * Date | Change *-----------+----------------------------------------------------------------- * 31-Dec-85 | Created changelog * 31-Dec-85 | Add c:\ to include directives * 31-Dec-85 | Changed to use new CBREAK as well as kbhit * 1-Jan-86 | Added stop_reason * 1-Jan-86 | Require musicfns.h before adagio.h * | stop_time is now long * | Turn off all notes in moreclean * | changed control logic in play * 1-Jan-86 | Added miditrace * 1-Jan-86 | Added command scanner at play request level * 18-Jan-86 | Gave priority to note offs. * | Created stop_check routine. * 21-Jan-86 | Added mpu_error_check to main loop * 3-Feb-86 | Changed help message slightly * 24-Mar-86 | Changed representation from note_struct to event_struct * 16-Jul-86 | Only pass upper case letters to tolower() * 7-Aug-86 | Initialize bend, touch, porta, mod, and foot controls *****************************************************************************/ #include "cext.h" #include "stdio.h" #include "ctype.h" #include "adagio.h" #include "mpu.h" #include "userio.h" #include "phase2.h" #define stop_code ' ' char stop_explanation[] = "Space bar"; extern int musictrace; extern int miditrace; extern int CBREAK; #define STOP_CC 1 #define STOP_CBRK 2 #define STOP_KEY 3 private long offset = 100; /* time offset from clock to performance */ private int program[num_voices]; /* current program */ /**************************************************************************** * Routines local to this module ****************************************************************************/ private void init_stuff(); private void moreclean(); private void play(); private void play_help(); private void print_status(); private boolean stop_check(); /**************************************************************************** * init_stuff * Effect: initializes this module (called by phase2()). ****************************************************************************/ private void init_stuff() { off_init(); } /**************************************************************************** * moreclean * Inputs: * event_type root: the score that was in progress * int stop_reason: the reason the score was stopped * Effect: * prints a message to announce stopped state * tells what was playing at the stop time * Implementation: * What was playing is determined by reading the time and searching * for notes that are on at that time. If nothing is found on a * given channel, it would be nice to print the last note played * on the channel provided it was played within say 1 sec of the * stop time. This is unimplemented, but would help locate short * notes. ****************************************************************************/ private void moreclean(score, stop_reason) event_type score; int stop_reason; { long stop_time; /* time at which moreclean is called */ event_type n; /* used to search the score */ stop_time = gettime() - offset; /* get current time */ while (note_offs(0x7fffffff)) ; /* turn off all notes */ printf ("\n * * STOPPED * *\n"); switch(stop_reason) { /* reason for stopping */ case STOP_CC: printf("Control-C seen\n"); break; case STOP_CBRK: printf("Control-break seen\n"); break; case STOP_KEY: printf("%s hit\n",stop_explanation); break; } /* reason for stopping */ if (stop_time > 0) printf ("Stopped at time = %ld (hundreths of a second). \n\n", stop_time); for (n = score; n != NULL; n = n->next) { if (is_note(n) && (n->ntime <= stop_time) && (n->ntime + n->u.note.ndur) >= stop_time) { printf("Voice: %d was playing line: %d\n", n->nvoice + 1, n->nline); } } } /**************************************************************************** * phase2 * Inputs: * event_type root: Root of play list * Effect: * Plays the music ****************************************************************************/ void phase2(root) event_type root; { char resp; short done = false; int truth; init_stuff(); while (!done) { /* ask user */ truth = true; printf ("\nType to play notes, q to quit, ? for help:"); while (true) { /* read input */ resp = getchar(); if (isupper(resp)) resp = tolower(resp); switch (resp) { /* decode */ case ' ': continue; /* ignore spaces */ case '?': play_help(); readln(stdin); break; case '-': truth = !truth; continue; /* read next char */ case 't': trace(truth); readln(stdin); break; case 'm': tracemidi(truth); readln(stdin); break; case 'q': done = true; readln(stdin); break; case 'r': readln(stdin); read_tuning(""); break; case 's': print_status(); readln(stdin); break; case '\n': CBREAK = 0; play(root); break; } /* decode */ break; } /* read input */ } /* ask user */ } /**************************************************************************** * play * Inputs: * event_type score: Score to play * Effect: * Plays the score ****************************************************************************/ private void play(score) event_type score; { event_type event = score; /* pointer to next note or control event */ short done = false; int stop_reason; /* ctrl-C, break, or space bar */ long time; /* the current time */ int i; /* index counter to initialize channels */ printf("Type %s to stop.\n",stop_explanation); musicinit(); /* Initialize all midi channels with reasonable start values: */ for (i = 1; i <= num_voices; i++) { midi_program(i, 1); program[i - 1] = 1; midi_bend(i, 1 << 13); midi_touch(i, 0); midi_ctrl(i, PORTARATE, 99); midi_ctrl(i, PORTASWITCH, 0); midi_ctrl(i, MODWHEEL, 0); midi_ctrl(i, FOOT, 99); } timereset(); l_restuntil(offset); while (!done) { /* play it, Sam */ time = gettime() - offset; /* delay everything by offset */ done = true; if (note_offs(time)) done = false; /* still more note offs */ if (event != NULL) { /* something to play */ done = false; if (time >= event->ntime) { if (is_note(event)) { /* play a note */ /* check for correct program (preset) */ if (event->u.note.nprogram != program[event->nvoice]) { midi_program(event->nvoice+1, event->u.note.nprogram); program[event->nvoice] = event->u.note.nprogram; } /* if it is a note (not a rest) play it */ if (event->u.note.npitch != NO_PITCH) { midi_note(event->nvoice+1, event->u.note.npitch, event->u.note.nloud); off_schedule(event->ntime + event->u.note.ndur, event->nvoice, event->u.note.npitch); } } else { /* send control info */ switch (vc_ctrl(event->nvoice)) { case 1: midi_ctrl(vc_voice(event->nvoice) + 1, PORTARATE, event->u.ctrl.value); break; case 2: midi_ctrl(vc_voice(event->nvoice) + 1, PORTASWITCH, event->u.ctrl.value); break; case 3: midi_ctrl(vc_voice(event->nvoice) + 1, MODWHEEL, event->u.ctrl.value); break; case 4: midi_touch(vc_voice(event->nvoice) + 1, event->u.ctrl.value); break; case 5: midi_ctrl(vc_voice(event->nvoice) + 1, FOOT, event->u.ctrl.value); break; case 6: midi_bend(vc_voice(event->nvoice) + 1, event->u.ctrl.value << 6); break; default: break; } } event = event->next; } else if (CBREAK || kbhit()) { done = stop_check(&stop_reason); if (done) { /* clean up */ moreclean(score, stop_reason); } /* clean up */ } else mpu_error_check(); } /* something to play */ } /* play it, Sam */ musicterm(); } /**************************************************************************** * play_help * Effect: * Lists help for play option ****************************************************************************/ private void play_help() { fprintf(stderr," Play music\n"); fprintf(stderr," q Quit Adagio (Exit)\n\n"); fprintf(stderr," r Read tuning file\n"); fprintf(stderr," m Turn on MIDI byte trace\n"); fprintf(stderr,"-m Turn off MIDI byte trace\n"); fprintf(stderr," s Report state\n"); fprintf(stderr," t Turn on music operation trace\n"); fprintf(stderr,"-t Turn off music operation trace\n"); fprintf(stderr," ? This message\n"); } /**************************************************************************** * print_status * Effect: * Informative output about state ****************************************************************************/ private void print_status() { fprintf(stderr,"MIDI trace (m option) %s\n",(miditrace ? "on" : "off")); fprintf(stderr,"Music trace (t option) %s\n",(musictrace ? "on" : "off")); } /**************************************************************************** * stop_check * Outputs: * *reason is set to reason for stop * true is returned iff play should stop * Effect: * Checks for break character or stop code from kbd ****************************************************************************/ private boolean stop_check(reason) int *reason; { boolean done = false; switch (CBREAK) { /* stop reason */ case 0: /* no stop code, try keyboard */ done = (getch() == stop_code); if (done) *reason = STOP_KEY; break; case 1: /* ctrl-break */ done = true; if (kbhit()) getch(); *reason = STOP_CBRK; break; case 2: /* ctrl-C */ done = true; *reason = STOP_CC; if (kbhit()) getch(); break; } /* stop reason */ return done; }