home *** CD-ROM | disk | FTP | other *** search
- /* record.c -- keyboard to adagio recorder
- *
- * the interface consists of three routines:
- * rec_init() -- initialization
- * int rec_poll(long time) -- called during recording, returns true if
- * recording space is exhausted
- * rec_final() -- called to finish up
- */
-
- /*****************************************************************************
- * Change Log
- * Date | Change
- *-----------+-----------------------------------------------------------------
- * 27-Feb-86 | Created changelog
- * | Use pedal information when computing durations (code taken
- * | from transcribe.c)
- * 23-Mar-86 | Determine size of transcription when rec_init is called.
- * 21-May-86 | Major rewrite to use continuous controls (code taken
- * | from transcribe.c)
- *****************************************************************************/
-
- #include "cext.h"
- #include "stdio.h"
- #include "malloc.h"
- #include "mpu.h"
- #include "userio.h"
- #include "midicode.h"
- #include "record.h"
-
- extern long space; /* how much space is left? */
-
- int debug_rec = false; /* verbose debug flag for this module */
- int max_notes = -1; /* -1 is flag that space must be allocated */
-
- /****************************************************************
- * data structure notes: the midi stream is stored as an array
- * of 4-byte records, each of which is either a time or midi
- * data. Midi data always begins with a control byte (high
- * order bit set), and it is assumed times are positive (high
- * order bit clear), so the two are easy to distinguish
- * IF THE COMPILER PUTS THESE BITS IN THE SAME PLACE. It looks
- * like the high order byte of the time lines up with the last
- * byte of a 4 byte array, so we will always set the high order
- * bit of the last array byte when the first 3 bytes are filled
- * with MIDI data. This is refered to as the "tag" bit.
- * WARNING: Lattice C longs are UNSIGNED, therefore always
- * positive. Test the high order bit with a mask.
- ****************************************************************/
-
- #define MIDI_CMD_BIT 0x80
- #define HIGH_BIT 0x80000000
- #define istime(note) (!(((note)->when) & HIGH_BIT))
-
- #define ndsw 2
- private char *dsw[ndsw] = { "-d", "-debug" };
-
- typedef union note_struct {
- byte n[4];
- long when;
- } *note_type, note_node;
-
- private note_type event_buff; /* pointer to allocated buffer */
- private FILE *fp;
- private char file_name[100];
- private note_type next; /* pointer to next entry in buffer */
- private note_type last; /* pointer to last entry in buffer */
- private int pile_ups; /* inner loop iteration count */
- private int max_pile; /* maximum of pile_ups */
-
- /****************************************************************************
- * Routines local to this module
- ****************************************************************************/
- private void bend_filter();
- private void byteorder();
- private void ctrl_filter();
- private int event_bend();
- private void filter();
- private long getdur();
- private long getnext();
- private char map_ctrl();
- private void output();
- private void put_pitch();
-
- /****************************************************************************
- * bend_filter
- * Inputs:
- * note_type note: the current note
- * note_type last: the last recorded event
- * long now: the current time
- * Effect:
- * remove pitch bend events in same 0.01 sec time slot
- * Implementation:
- * If the current event is a pitch bend that bends again
- * in the same time slot, make it a no-op by replacing it with
- * the time.
- ****************************************************************************/
-
- private void bend_filter(note, last, now)
- note_type note; /* current note */
- note_type last; /* the last recorded event */
- long now; /* the current time */
- {
- /* first see if there is another bend in this time
- * slot.
- */
- note_type note2 = note + 1;
- while (note2 < last) {
- if (istime(note2) && (note2->when > now)) {
- break; /* new time slot */
- } else if (note->n[0] == note2->n[0]) {
- note->when = now;
- return; /* found another bend */
- }
- note2++;
- }
- }
-
- /****************************************************************************
- * byteorder
- * Effect:
- * check out assumptions about byte order and placement
- ****************************************************************************/
-
- private void byteorder()
- {
- if ((sizeof(event_buff[0]) != 4) ||
- (sizeof(event_buff[0].when) != 4) ||
- (sizeof(event_buff[0].n[0]) != 1)) {
- fprintf(stderr, "implementation error: size problem\n");
- exit(1);
- }
- event_buff[0].n[0] = 0x12;
- event_buff[0].n[1] = 0x34;
- event_buff[0].n[2] = 0x56;
- event_buff[0].n[3] = 0x78;
- if ((event_buff[0].when != 0x78563412) &&
- (event_buff[0].when != 0x12345678)) {
- fprintf(stderr, "implementation error: layout problem\n");
- exit(1);
- }
- }
-
- /****************************************************************************
- * ctrl_filter
- * Inputs:
- * note_type note: the current note
- * note_type last: the last recorded event
- * long now: the current time
- * Effect:
- * remove ctrl change events in same 0.01 sec time slot
- * Implementation:
- * If the current event is a control change that changes again
- * in the same time slot, make it a no-op by replacing it with
- * the time.
- ****************************************************************************/
-
- private void ctrl_filter(note, last, now)
- note_type note; /* the current note */
- note_type last; /* the last recorded event */
- long now; /* the current time */
- {
- /* see if there is another control change in this time
- * slot.
- */
- note_type note2 = note+1;
- while (note2 < last) {
- if (istime(note2) && (note2->when > now)) {
- break; /* new time slot */
- } else if ((note->n[0] == note2->n[0]) &&
- (note->n[1] == note2->n[1])) {
- note->when = now;
- return; /* found another change */
- }
- note2++;
- }
- }
-
- /****************************************************************************
- * event_bend
- * Inputs:
- * note_type note: pointer to a pitch bend event
- * Outputs:
- * returns int: an 8 bit pitch bend number
- ****************************************************************************/
-
- private int event_bend(note)
- note_type note;
- {
- return (int) (((note->n[1]) >> 6) + ((note->n[2]) << 1));
- }
-
- /****************************************************************************
- * filter
- * Inputs:
- * note_type last: the last note recorded
- * Effect: allow only one control change per time slot (0.01 sec)
- * Implementation:
- * call ctrl_filter and bend_filter to overwrite control changes with
- * noop data (the current time is used as a noop)
- ****************************************************************************/
-
- private void filter(last)
- note_type last;
- {
- note_type note; /* loop control variable */
- long now; /* last time seen */
- int command; /* command pointed to by note */
- int chan; /* channel pointed to by note */
-
- for (note = event_buff; note <= last; note++) {
- if (istime(note)) {
- now = note->when;
- } else {
- command = note->n[0] & MIDI_CODE_MASK;
- chan = note->n[0] & MIDI_CHN_MASK;
-
- if (command == MIDI_CTRL &&
- note->n[1] == SUSTAIN) {
- /* do nothing */;
- } else if (command == MIDI_CTRL) {
- ctrl_filter(note, last, now);
- } else if (command == MIDI_TOUCH) {
- bend_filter(note, last, now); /* bend and touch use the */
- } else if (command == MIDI_BEND) { /* same filter routines */
- bend_filter(note, last, now);
- }
- }
- }
- }
-
-
- /****************************************************************************
- * getdur
- * Inputs:
- * int i: index of the note
- * note_type last: pointer to the last event recorded
- * int ped: true if pedal is down at event i
- * long now: the time at event i
- * Outputs:
- * returns long: the duration of note i
- * Assumes:
- * assumes i is a note
- * Implementation:
- * This is tricky because of pedal messages. The note is kept on by
- * either the key or the pedal. Keep 2 flags, key and ped. Key is
- * turned off when a key is released, ped goes off and on with pedal.
- * Note ends when (1) both key and ped are false, (2) key is
- * pressed (this event will also start another note).
- ****************************************************************************/
-
- private long getdur(i, last, ped, now)
- int i;
- note_type last;
- int ped;
- long now;
- {
- int key = true; /* flag that says if note is on */
- long start = now;
- int chan = event_buff[i].n[0] & MIDI_CHN_MASK;
- int pitch = event_buff[i].n[1];
- note_type note = &(event_buff[i+1]);
- int noteon; /* true if a noteon message received on chan */
- int keyon; /* true if noteon message had non-zero velocity */
-
- /* search from the next event (i+1) to the end of the buffer:
- */
- for (; note < last; note++) {
- if (istime(note)) {
- now = note->when;
- } else {
- noteon = keyon = false;
- if ((note->n[0] & MIDI_CHN_MASK) == chan) {
- noteon = ((note->n[0] & MIDI_CODE_MASK) == MIDI_ON_NOTE) &&
- (note->n[1] == pitch);
- keyon = noteon && (note->n[2] != 0);
- if ((noteon && (note->n[2] == 0)) ||
- (((note->n[0] & MIDI_CODE_MASK) == MIDI_OFF_NOTE) &&
- (note->n[1] == pitch))) key = false;
- if (((note->n[0] & MIDI_CODE_MASK) == MIDI_CTRL) &&
- note->n[1] == SUSTAIN && note->n[2] == 127) ped = true;
- if (((note->n[0] & MIDI_CODE_MASK) == MIDI_CTRL) &&
- note->n[1] == SUSTAIN && note->n[2] == 0) ped = false;
-
- if ((!key && !ped) || keyon)
- return now - start;
- }
- }
- }
- return last->when - start;
- }
-
- /****************************************************************************
- * getnext
- * Inputs:
- * int i: the index of the current note
- * note_type last: pointer to last valid data
- * long now: the current time
- * Outputs:
- * returns long: the time of the next note, program, or control change
- * (returns time of last event if nothing else is found)
- ****************************************************************************/
-
- private long getnext(i, last, now)
- int i; /* the index of the current note */
- note_type last; /* pointer to last valid data */
- long now; /* the current time */
- {
- i++; /* advance to next item */
- for (; event_buff + i < last; i++) {
- note_type note = &(event_buff[i]);
- int cmd = note->n[0] & MIDI_CODE_MASK;
-
- if (istime(note)) {
- now = note->when;
- } else if (((cmd == MIDI_ON_NOTE) &&
- (note->n[2] != 0)) /* note on */ ||
- (cmd == MIDI_CH_PROGRAM) /* program change */ ||
- ((cmd == MIDI_CTRL) &&
- (note->n[1] != SUSTAIN) /* control change */ ) ||
- (cmd == MIDI_TOUCH) ||
- (cmd == MIDI_BEND)) {
- return now;
- }
- }
- return last->when;
- }
-
- /****************************************************************************
- * map_ctrl
- * Inputs:
- * int control: a midi control number
- * Outputs:
- * returns char: an adagio control change command letter, NULL if
- * control change is not one of PORTARATE, PORTASWITCH,
- * MODWHEEL, FOOT
- ****************************************************************************/
-
- private char map_ctrl(control)
- int control;
- {
- switch (control) {
- case PORTARATE: return 'J';
- case PORTASWITCH: return 'K';
- case MODWHEEL: return 'M';
- case FOOT: return 'X';
- default: return 0;
- }
- return NULL; /* make Lattice C type cheker happy */
- }
-
- /****************************************************************************
- * output
- * Inputs:
- * FILE *fp: an opened file pointer
- * note_type last: the last data in the buffer
- * boolean absflag: set to true if first line of the adagio score should
- * include the absolute time
- * Effect:
- * write adagio file using data in event_buff
- * Implementation:
- * NOTE: put all program changes in rests
- * use N(ext) notation for all timing
- * output no more than one continuous parameter change per
- * clock tick for each continuous change parameter
- ****************************************************************************/
-
- private void output(fp, last, absflag)
- FILE *fp;
- note_type last;
- boolean absflag;
- {
- int i; /* loop counter */
- int command; /* the current command */
- int chan; /* the midi channel of the current event */
- int lastchan = 0; /* the default adagio channel (1) */
- int ped = false; /* flag maintains state of pedal */
- int how_many = last - event_buff;
- long now; /* the time of the next event */
- boolean bad_ctrl_flag = false; /* set to true if unknown ctrl read */
-
- if (fp == NULL) {
- fprintf(stderr, "internal error: output called with NULL file.\n");
- exit(1);
- }
-
- if (debug_rec)
- printf("hint: if file is not being closed, decrease MAXSPACE\n");
-
- /* set the initial absolute time, all other times are relative */
- if (absflag)
- fprintf(fp, "t%ld ", event_buff[0].when);
-
- for (i = 0; i < how_many; i++) {
- if (debug_rec) {
- printf("ev %d: %x %x %x (%ld)\n", i, event_buff[i].n[0],
- event_buff[i].n[1], event_buff[i].n[2], event_buff[i].when);
- }
-
- if (istime(event_buff+i)) {
- now = event_buff[i].when;
- if (debug_rec) printf("i = %d, now = %ld\n", i, now);
- } else {
- command = event_buff[i].n[0] & MIDI_CODE_MASK;
- chan = event_buff[i].n[0] & MIDI_CHN_MASK;
-
- if (command == MIDI_ON_NOTE && event_buff[i].n[2] != 0) {
- put_pitch(fp, event_buff[i].n[1] - 12);
- fprintf(fp, " u%ld l%d n%ld", getdur(i, last, ped, now),
- event_buff[i].n[2], getnext(i, last, now) - now);
- if (lastchan != chan) {
- fprintf(fp, " v%d\n", chan + 1);
- lastchan = chan;
- } else fprintf(fp, "\n");
- } else if (command == MIDI_CH_PROGRAM) {
- fprintf(fp, "r z%d n%ld", event_buff[i].n[1] + 1,
- getnext(i, last, now) - now);
- if (lastchan != chan) {
- fprintf(fp, " v%d\n", chan + 1);
- lastchan = chan;
- } else fprintf(fp, "\n");
- } else if (command == MIDI_CTRL &&
- event_buff[i].n[1] == SUSTAIN) {
- ped = (event_buff[i].n[2] != 0);
- } else if (command == MIDI_CTRL) {
- char c = map_ctrl(event_buff[i].n[1]);
- if (c != 0) {
- fprintf(fp, "%c%d n%d\n", c,
- event_buff[i].n[2], getnext(i, last, now) - now);
- } else bad_ctrl_flag = true;
- } else if (command == MIDI_TOUCH) {
- fprintf(fp, "O%d n%d\n", event_buff[i].n[1],
- getnext(i, last, now) - now);
- } else if (command == MIDI_BEND) {
- fprintf(fp, "Y%d n%d\n", event_bend(&event_buff[i]),
- getnext(i, last, now) - now);
- } else if (command != MIDI_ON_NOTE) {
- fprintf(stderr, "Command 0x%x ignored\n", command);
- }
- }
- }
- if (bad_ctrl_flag)
- fprintf(stderr,
- "Some unrecognized control changes were omitted from file.\n");
- }
-
- /****************************************************************************
- * put_pitch
- * Inputs:
- * FILE *fp: an open file
- * int p: a pitch number
- * Effect: write out the pitch name for a given number
- ****************************************************************************/
-
- private void put_pitch(fp, p)
- FILE *fp;
- int p;
- {
- static char *ptos[] = {"c", "cs", "d", "ef", "e", "f", "fs", "g",
- "gs", "a", "bf", "b"};
- fprintf(fp, "%s%d", ptos[p % 12], p / 12);
- }
-
- /**********************************************************************
- * rec_final
- * Inputs:
- * boolean absflag: output absolute time of first note if true
- * Effect:
- * Write recorded data to a file
- **********************************************************************/
-
- void rec_final(absflag)
- boolean absflag;
- {
- next->when = gettime();
- last = next;
- if (debug_rec) printf("max_pile_up = %d, ", max_pile);
- printf("%d times and events recorded.\n", last - event_buff);
- filter(last);
- output(fp, last, absflag);
- fclose(fp);
- }
-
- /****************************************************************************
- * rec_init
- * Inputs:
- * char *file: pointer to file name from command line (if any)
- * boolean bender: true if pitch bend should be enabled
- * Outputs:
- * returns true if initialization succeeds
- * Effect:
- * prepares module to record midi input
- ****************************************************************************/
-
- boolean rec_init(file, bender)
- char *file;
- boolean bender;
- {
- /* (char *)malloc(); */ /* memory allocation */
-
- debug_rec = (cl_nswitch(dsw, ndsw) != NULL);
-
- pile_ups = 0;
- max_pile = 0;
-
- fp = fileopen(file, "gio", "w", "Name of output file");
- if (max_notes == -1) { /* allocate space 1st time rec_init called */
- max_notes = space/sizeof(note_node);
- event_buff = (note_type) malloc(sizeof(note_node) * max_notes);
- if (event_buff == NULL) {
- fprintf(stderr, "Internal error allocating record space.");
- musicterm();
- exit(1);
- }
- byteorder();
- printf("Space for %d events has been allocated.\n", max_notes);
- }
- next = event_buff;
- last = event_buff + max_notes - 2;
-
- while (getkey(false) != -1) ; /* flush old midi events */
- midi_cont(bender);
- return max_notes > 10;
- }
-
- /****************************************************************************
- * rec_poll
- * Inputs:
- * long time: the current time
- * Outputs:
- * returns true if there is no more memory
- * Effect: reads and stores any input
- * Assumes: rec_poll must be called frequently to get accurate results
- * Implementation:
- * time stamps and midi events share the same buffer of 4-byte events
- * save time at most once per call to rec_poll
- * save time only if midi data is present
- ****************************************************************************/
-
- boolean rec_poll(time)
- long time;
- {
- next->when = time; /* this will overwrite an earlier time unless data */
- /* was recorded */
- if (getbuf(false, (next+1)->n)) { /* buffer nonempty? */
- next++;
- next->n[3] = MIDI_CMD_BIT; /* set tag bit */
- pile_ups = 1;
- while (getbuf(false, (++next)->n)) {
- next->n[3] = MIDI_CMD_BIT; /* set tag bit */
- pile_ups++;
- if (next >= last) {
- break;
- }
- }
- }
- if (pile_ups > max_pile) max_pile = pile_ups;
- if (next >= last) {
- fprintf(stderr, "No more memory.\n");
- return true;
- } /* else */ return false;
- }
-