home *** CD-ROM | disk | FTP | other *** search
- From: arensb@alv.umd.edu (Andrew Arensburger)
- Newsgroups: comp.sources.misc
- Subject: v17i002: midifile - A MIDI file parser, Part01/01
- Message-ID: <1991Feb18.225411.5540@sparky.IMD.Sterling.COM>
- Date: 18 Feb 91 22:54:11 GMT
- Approved: kent@sparky.imd.sterling.com
- X-Checksum-Snefru: 0b871ef3 2004531f f537b92b d9dbacef
-
- Submitted-by: Andrew Arensburger <arensb@alv.umd.edu>
- Posting-number: Volume 17, Issue 2
- Archive-name: midifile/part01
-
- This is 'midifile()', a parser for MIDI files. This is functionally
- identical to the 'midifile()' written by Tim Thompson, and recently posted
- to the net. I wasn't quite satisfied with his implementation, so I rewrote it.
-
- midifile() reads in a MIDI format file, and calls user-defined routines each
- time it encounters an event. You can ignore any events you're not interested
- in, and concentrate on the ones you want. In fact, you can even ignore the
- tracks you're not interested in.
-
- Why should you use my version? This midifile() deals less traumatically with
- errors: by default, it returns an error code to the calling routine, instead
- of exit()ing the program (so you can do that with the original, too. Big deal).
- Also, this version allows you to return an error code if and when you do
- interrupt the routine's execution. I've added a new variable: Mf_deltatime,
- which gives the time elapsed since the previous event. A complete and (hopefully)
- accurate man page is also supplied.
-
- Why should you use the original version (Classic midifile())? Because you have
- system-exclusive events in your MIDI file. I haven't implemented these, but will
- do so in the next version, scheduled to come out RSN.
-
- Andrew Arensburger
- arensb@cvl.umd.edu
-
- ----- CUT HERE -------------------------------------------------------
- : Run this shell script with "sh" not "csh"
- PATH=:/bin:/usr/bin:/usr/ucb
- export PATH
- all=FALSE
- if [ x$1 = x-a ]; then
- all=TRUE
- fi
- /bin/echo 'Making directory "midifile"'
- mkdir midifile
- /bin/echo 'Extracting midifile/Makefile'
- sed 's/^X//' <<'//go.sysin dd *' >midifile/Makefile
- # Makefile for 'midifile()'. It's pretty trivial, really.
-
- all: midifile
-
- midifile: mf.c mf.h
- cc -c mf.c
- //go.sysin dd *
- if [ `wc -c < midifile/Makefile` != 106 ]; then
- made=FALSE
- /bin/echo 'error transmitting "midifile/Makefile" --'
- /bin/echo 'length should be 106, not' `wc -c < midifile/Makefile`
- else
- made=TRUE
- fi
- if [ $made = TRUE ]; then
- /bin/chmod 664 midifile/Makefile
- /bin/echo -n ' '; /bin/ls -ld midifile/Makefile
- fi
- /bin/echo 'Extracting midifile/mf.c'
- sed 's/^X//' <<'//go.sysin dd *' >midifile/mf.c
- X/* MF.C v.1.2
- * A MIDI file analyzer. The routine 'midifile()' reads in a MIDI file,
- * calling external routines as it goes along, processing it. 'midifile()'
- * returns 0 if all went well, and an error code otherwise.
- * This program is based on Tim Thompson's 'midifile()'. I rewrote
- * it because I didn't like the way he did it. This one should be 100%
- * compatible with his.
- * The differences between this 'midifile()' and Thompson's are as
- * follows: this one does not 'exit()' from the program as soon as it finds
- * an error. Instead, it causes the main routine, 'midifile()' to return
- * a negative error code.
- * In addition to 'Mf_currtime', which gives the time elapsed since
- * the beginning of the track, I have added another variable: 'Mf_deltatime',
- * which gives the time elapsed since the last event. This is because the
- * MPU-401 requires delta-times, and not absolute times.
- *
- * IMPLEMENTATION NOTES:
- * About 'bailout()': this macro calls the user-defined error routine,
- * passing it an error message.
- * a) DO NOT PUT A SEMICOLON AFTER IT, i.e.
- * bailout(MFE_EOF)
- * b) It should be called as soon as the error is detected, and
- * not at lower levels, otherwise, the error routine will be called
- * several times for the same error. A routine is expected to return
- * a negative value in case of error, so test for that first.
- */
-
- #include <stdio.h>
- #include "mf.h"
-
- extern char *malloc();
-
- typedef unsigned char uchar;
- typedef unsigned long ulong;
- typedef unsigned short ushort;
-
- long Mf_currtime; /* Current time */
- long Mf_deltatime; /* Time since last event */
- int eot_called; /* Flag: has 'Mf_eot()' been called? */
-
- X/* NUM_PARAMS
- * Returns the number of parameters for a given status byte, or -1 for
- * SysEx and meta-messages, which have a variable number of arguments.
- */
- static int num_params(stat)
- uchar stat;
- {
- if (stat >= MIDI_SYSEX)
- return(-1); /* SysEx or meta */
- if (stat < MIDI_PROGRAM || stat >= MIDI_PITCHB)
- return(2);
- else
- return(1);
- }
-
- X/* READ_LONG
- * Reads a 32-bit unsigned number and returns it. MSB first.
- * Returns an error code upon error.
- */
- static long read_long()
- {
- ulong retval;
- int i;
- int c;
-
- retval = 0L; /* Set 'retval to 0 initially */
- for (i = 0; i < 4; i++) /* Read in 4 bytes into 'retval' */
- {
- if ((c = (*Mf_getc)()) == -1)
- bailout(MFE_EOF)
- else {
- retval <<= 8;
- retval |= (uchar) c;
- }
- }
-
- return(retval); /* All went well */
- }
-
- X/* READ_SHORT
- * Reads a 16-bit number, MSB first, and returns it.
- * Returns an error code upon error.
- */
- static short read_short()
- {
- ushort retval;
- int i;
- int c;
-
- retval = 0x00; /* Set 'retval to 0 initially */
- for (i = 0; i < 2; i++) /* Read in 2 bytes into 'retval' */
- {
- if ((c = (*Mf_getc)()) == -1)
- bailout(MFE_EOF)
- else {
- retval <<= 8;
- retval |= (uchar) c;
- }
- }
-
- return(retval); /* All went well */
- }
-
- X/* STOL
- * Converts a string of 'len' bytes, MSB first, into a long int. This is
- * not to be confused with 'atoi()': 'atoi()' converts "abcdef12" into
- * '0xabcdef12L', whereas 'stol()' converts '0xab,0xcd,0xef,0x12' into
- * '0xabcdef12L'.
- */
- ulong stol(str,len)
- uchar *str;
- int len;
- {
- int i;
- long retval;
-
- retval = 0L;
- for (i = 0; i < len; i++)
- {
- retval <<= 8;
- retval |= str[i];
- }
-
- return(retval);
- }
-
- X/* READ_VARLEN
- * Reads a variable-length number, and returns it.
- * Returns an error code upon error.
- * This routine is copied almost verbatim from the MIDI 0.6 file specs.
- */
- static long read_varlen(toberead)
- long *toberead;
- {
- long value;
- int c;
-
- if ((value = (*Mf_getc)()) < 0)
- bailout(value) /* Return error code */
- else {
- if (--*toberead < 0)
- bailout(MFE_EOF); /* Error: number expected */
-
- if (value & 0x80)
- {
- value &= 0x7f;
- do
- {
- if ((c = (*Mf_getc)()) < 0)
- bailout(c) /* Return error code */
- else
- if (--*toberead < 0)
- bailout(MFE_EOF);
- value = (value << 7) + (c & 0x7f);
- } while (c & 0x80);
- }
- }
-
- return(value);
- }
-
- X/* READ_HEADER
- * Reads in the header from the input file, using the function 'Mf_getc()'.
- * Returns the number of tracks in the file if all goes well, or an error
- * code otherwise (including if the user disapproves).
- */
- static int read_header()
- {
- int i;
- static char kw_buff[4]; /* Buffer for input keyword "MThd" */
- long header_len; /* Header length */
- short format; /* File format */
- short ntrks; /* Number of tracks in the file */
- short division; /* Quarter-note division */
- int retval = 0;
-
- /* Read in the first four characters into 'kw_buff' */
- for (i = 0; i < 4; i++)
- {
- int c; /* Input character */
-
- if ((c = (*Mf_getc)()) == -1)
- bailout(MFE_EOF)
- else
- kw_buff[i] = (char) c;
- }
-
- /* See if 'kw_buff' contains "MThd" */
- if (strncmp(kw_buff,"MThd",4) != 0)
- bailout(MFE_HKWEXP)
-
- /* Read the number 6 (header length) from the header */
- if ((header_len = read_long()) < 0)
- return(header_len); /* Return error code */
- else if (header_len != 6L)
- bailout(MFE_ILLHLEN) /* Incorrect header length */
-
- /* Read the file format, number of tracks, and quarter-note division */
- if ((format = read_short()) < 0)
- bailout(format)
- if (format > 2) /* Illegal file format */
- bailout(MFE_ILLFORM)
- if ((ntrks = read_short()) < 0)
- return(ntrks); /* Return error code */
- if ((division = read_short()) < 0)
- return(division); /* Return error code */
-
- /* Pass on the header info to the user */
- if (Mf_header == NULL ||
- (retval = (*Mf_header)(format,ntrks,division)) >= 0)
- return(ntrks);
- else
- return(retval); /* User already knows about error */
- }
-
- X/* READ_SYSEX
- * Read a system-exclusive/meta message, and pass it on to the user.
- * Warning: the buffer in which the SysEx data is stored will be reclaimed,
- * so the user is responsible for storing it if it is needed.
- * BTW, I'm doing some funky stuff with 'Mf_deltatime', so read the
- * comments at the beginning of 'read_event()'.
- */
- static int read_sysex(stat,toberead)
- uchar stat; /* Type of SysEx message */
- long *toberead; /* # bytes remaining to be read in track */
- {
- char *data; /* Where the data is kept */
- long length; /* Length of message */
- long i;
-
- if (stat == 0xff) /* Meta message */
- {
- int msg_type; /* Type of meta-event */
-
- /* Read type of meta-event */
- if ((msg_type = (*Mf_getc)()) < 0 ||
- --*toberead < 0)
- bailout(MFE_EOF) /* Event type expected */
-
- /* Read length of meta-event */
- if ((length = read_varlen(toberead)) < 0)
- return((int) length);
-
- /* Allocate memory for meta-event */
- if (length != 0L &&
- (data = malloc((unsigned) length)) == NULL)
- bailout(MFE_MEM)
- else if (length == 0L)
- data = NULL;
-
- /* Read in the meta-event */
- for (i = 0; i < length; i++)
- {
- int c; /* Input character */
-
- if ((c = (*Mf_getc)()) < 0 ||
- --*toberead < 0)
- {
- free(data); /* Free up the memory */
- bailout(MFE_EOF)
- }
- data[i] = (char) c;
- }
-
- /* Take action according to the type of meta-event.
- * In particular, make sure that the length of the
- * meta-event is correct (where applicable).
- */
- switch(msg_type) {
- case META_SEQNUM: /* Sequence number */
- {
- short seqnum;
- int retval;
-
- if (length != 2) /* Check length */
- bailout(MFE_ILLMLEN)
-
- seqnum = ((short) data[0] << 8) |
- data[1];
- if (Mf_seqnum != NULL &&
- (retval = (*Mf_seqnum)(seqnum),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- free(data);
- return(retval);
- }
-
- break;
- }
-
- case META_TEXT: /* Text event */
- case META_COPYRIGHT: /* Copyright notice */
- case META_SEQNAME: /* Sequence name */
- case META_INSTNAME: /* Instrument name */
- case META_LYRIC: /* Lyric */
- case META_MARKER: /* Marker */
- case META_CUEPT: /* Cue point */
- {
- int retval;
-
- if (Mf_text != NULL &&
- (retval = (*Mf_text)((int) msg_type,
- length,data),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- if (data != NULL)
- free(data);
- return(retval);
- }
-
- break;
- }
- case META_EOT: /* End of track */
- {
- int retval;
-
- if (length != 0L) /* Check length */
- bailout(MFE_ILLMLEN)
-
- if (Mf_eot != NULL &&
- (retval = (*Mf_eot)(),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- free(data);
- return(retval);
- }
- eot_called = 1;
-
- break;
- }
-
- case META_TEMPO: /* Set tempo */
- {
- ulong tempo;
- int retval;
-
- if (length != 3) /* Check length */
- bailout(MFE_ILLMLEN)
-
- tempo = /**(ushort *) data;*/
- stol(data,3);
- if (Mf_tempo != NULL &&
- (retval = (*Mf_tempo)(tempo),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- free(data);
- return(retval);
- }
-
- break;
- }
- case META_SMPTE: /* SMPTE offset */
- {
- int retval;
- int hour,min,sec,frame,fract;
-
- if (length != 5)
- bailout(MFE_ILLMLEN)
- hour = (int) data[0];
- min = (int) data[1];
- sec = (int) data[2];
- frame = (int) data[3];
- fract = (int) data[4];
- if (Mf_smpte != NULL &&
- (retval = (*Mf_smpte)(hour,min,
- sec,frame,fract),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- free(data);
- return(retval);
- }
-
- break;
- }
- case META_TIMESIG: /* Time signature */
- {
- int retval;
- int numer,denom,clocks,qnotes;
-
- if (length != 4)
- bailout(MFE_ILLMLEN)
- numer = (int) data[0];
- denom = (int) data[1];
- clocks = (int) data[2];
- qnotes = (int) data[3];
- if (Mf_timesig != NULL &&
- (retval = (*Mf_timesig)(numer,
- denom,clocks,qnotes),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- free(data);
- return(retval);
- }
-
- break;
- }
- case META_KEYSIG: /* Key signature */
- {
- int retval;
- int sharpflat,minor;
-
- if (length != 2)
- bailout(MFE_ILLMLEN)
- sharpflat = (int) data[0];
- minor = (int) data[1];
- if (sharpflat > 7 ||
- sharpflat < -7 ||
- (minor != 0 && minor != 1))
- bailout(MFE_ILLMVAL)
- if (Mf_keysig != NULL &&
- (retval = (*Mf_keysig)(sharpflat,
- minor),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- free(data);
- return(retval);
- }
-
- break;
- }
- case META_SEQSPEC: /* Sequencer-specific */
- {
- int retval;
-
- if (Mf_sqspecific != NULL &&
- (retval = (*Mf_sqspecific)(0,
- length,data),
- Mf_deltatime = 0L,
- retval) < 0)
- {
- if (data != NULL)
- free(data);
- return(retval);
- }
-
- break;
- }
- default:
- {
- int retval;
-
- if (Mf_error != NULL &&
- (retval = (*Mf_error)
- (err_msgs[MFE_UKMETA])) < 0)
- {
- if (data != NULL)
- free(data);
- return(retval);
- }
-
- break;
- }
- }
-
- /* All went well. Free 'data' if necessary, and return */
- if (data != NULL)
- free(data);
- return(MFE_OK);
- }
-
- if (stat == 0xf0) /* Syntactically complete message */
- {
- fprintf(stderr, "Sorry, no complete SysEx messages\n");
- }
-
- if (stat == 0xf7) /* Part of a complete SysEx message */
- {
- fprintf(stderr, "Sorry, no incomplete SysEx messages\n");
- }
-
- bailout(MFE_ILLSETYPE)
- }
-
- X/* READ_EVENT
- * Read an event, parse it by type, and pass the data to the user's
- * routines.
- * Returns MFE_OK if all goes well, or an error code otherwise.
- * A comment about the funky stuff I'm doing with 'Mf_deltatime':
- * it turned out that if the user didn't check for _every_ event,
- * then 'Mf_deltatime' got screwed up. In other words, if you didn't
- * trap pitch-bends, then any pitch-bends in the file, >>> and their
- * associated deltas <<< would be ignored. This is obviously a badism.
- * To solve this, 'Mf_deltatime' is always incremented by the current
- * delta, in 'read_event()'. Later, when the event is processed,
- * 'Mf_deltatime' gets reset to 0 iff the handling routine has been
- * called.
- * The specific mechanism is a bit funky, so here's an example:
- * if (Mf_off != NULL &&
- * (retval = (*Mf_off)(run_stat & 0x07,
- * params[0],
- * params[1]),
- * Mf_deltatime = 0,
- * retval) < 0)
- * return(retval);
- * This uses two particularities of C: lazy evaluation, and statements
- * separated by commas. If 'Mf_off' is NULL, then the event is not
- * important to the user, and 'Mf_deltatime' is kept intact for the next
- * event. Otherwise, the following sequence of events occurs:
- * - The user's routine is called, and the return value is stored
- * in 'retval'.
- * - 'Mf_deltatime' gets reset to 0.
- * - 'retval' is called to the forefront, just in time to see
- * whether the user's routine returned an error value.
- */
- static int read_event(toberead)
- long *toberead; /* # bytes left to be read in the track */
- {
- static uchar run_stat = 0x00; /* Running status */
- int stat; /* Current status */
- long delta_time; /* Time since last event */
- static uchar params[2]; /* Event parameters */
- int cur_param; /* Parameter being currently read */
- int i;
-
- /* Read delta-time */
- if ((delta_time = read_varlen(toberead)) < 0)
- return((int) delta_time); /* Return error code */
-
- /* Update 'Mf_currtime' and 'Mf_deltatime' */
- Mf_currtime += Mf_deltatime += delta_time;
-
- /* Read event type */
- if ((stat = (*Mf_getc)()) < 0 ||
- --*toberead < 0)
- bailout(MFE_EOF) /* Error: event type expected */
- if (stat & 0x80) /* Is it a new event type? */
- {
- run_stat = (uchar) stat; /* Set new running status */
- cur_param = 0; /* Start reading at 0th param */
- } else {
- params[0] = (uchar) stat; /* Record 1st parameter */
- cur_param = 1; /* Start reading at 1st param */
- }
-
- /* Read the parameters corresponding to the status byte */
- for (i = num_params(run_stat)-cur_param; i > 0; i--, cur_param++)
- {
- int c; /* Input character */
-
- if ((c = (*Mf_getc)()) < 0 ||
- --*toberead < 0)
- bailout(MFE_EOF); /* Premature end of file */
- params[cur_param] = (uchar) c; /* Record parameter */
- }
-
- /* Report event and parameters to user, depending on type of
- * event.
- */
- switch(run_stat & 0xf0) {
- int retval;
-
- case MIDI_NOTEOFF: /* Note off */
- if (Mf_off != NULL &&
- (retval = (*Mf_off)(run_stat & 0x07,
- params[0],
- params[1]),
- Mf_deltatime = 0,
- retval) < 0)
- return(retval); /* Return error code */
- return(MFE_OK);
-
- case MIDI_NOTEON: /* Note on */
- if (Mf_on != NULL &&
- (retval = (*Mf_on)(run_stat & 0x07,
- params[0],
- params[1]),
- Mf_deltatime = 0L,
- retval) < 0)
- return(retval); /* Return error code */
- return(MFE_OK);
-
- case MIDI_PRESSURE: /* Polyphonic key pressure */
- if (Mf_pressure != NULL &&
- (retval = (*Mf_pressure)(run_stat & 0x07,
- params[0],
- params[1]),
- Mf_deltatime = 0L,
- retval) < 0)
- return(retval); /* Return error code */
- return(MFE_OK);
-
- case MIDI_CONTROL: /* Control change */
- if (Mf_controller != NULL &&
- (retval = (*Mf_controller)(run_stat & 0x07,
- params[0],
- params[1]),
- Mf_deltatime = 0L,
- retval) < 0)
- return(retval); /* Return error code */
- return(MFE_OK);
-
- case MIDI_PROGRAM: /* Program change */
- if (Mf_program != NULL &&
- (retval = (*Mf_program)(run_stat & 0x07,
- params[0]),
- Mf_deltatime = 0L,
- retval) < 0)
- return(retval); /* Return error code */
- return(MFE_OK);
-
- case MIDI_CHANPRES: /* Channel pressure */
- if (Mf_chanpressure != NULL &&
- (retval = (*Mf_chanpressure)(run_stat & 0x07,
- params[0]),
- Mf_deltatime = 0L,
- retval) < 0)
- return(retval); /* Return error code */
- return(MFE_OK);
-
- case MIDI_PITCHB: /* Pitch wheel change */
- if (Mf_pitchbend != NULL &&
- (retval = (*Mf_pitchbend)(run_stat & 0x07,
- params[0],
- params[1]),
- Mf_deltatime = 0L,
- retval) < 0)
- return(retval); /* Return error code */
- return(MFE_OK);
-
- case MIDI_SYSEX: /* System-exclusive/meta */
- return(read_sysex(run_stat,toberead));
- default:
- bailout(MFE_ILLSTAT)
- }
- }
-
- X/* READ_TRACK
- * Reads in a track, parses it, and passes the various parts on to the
- * user's routines.
- * Returns MFE_OK if all goes well, or an error code if anything goes wrong.
- */
- static int read_track()
- {
- int i;
- static char kw_buff[4]; /* Buffer for input keyword "MTrk" */
- int error;
- long tklen; /* Track length */
-
- /* Reset 'Mf_currtime'. If the file format is 2, then it probably
- * doesn't matter anyway, but for the other types (single and
- * multiple tracks), 'Mf_currtime' should measure the time elapsed
- * since the beginning of the track.
- */
- Mf_currtime = 0L;
-
- eot_called = 0; /* Indicate that 'Mf_eot()' hasn't been called yet */
-
- /* Read in the first four characters into 'kw_buff' */
- for (i = 0; i < 4; i++)
- {
- int c; /* Input character */
-
- if ((c = (*Mf_getc)()) == -1)
- bailout(MFE_EOF)
- else
- kw_buff[i] = (char) c;
- }
-
- /* See if 'kw_buff' contains "MTrk". In some future version,
- * if the chunk type is unknown, then this section of code
- * should just ignore it.
- */
- if (strncmp(kw_buff,"MTrk",4) != 0)
- bailout(MFE_TKWEXP)
-
- /* Tell the user that we're starting a new track */
- if (Mf_starttrack != NULL &&
- (error = (*Mf_starttrack)()) < 0)
- return(error); /* User already knows about error */
-
- /* Read the length of the track */
- if ((tklen = read_long()) < 0)
- return(tklen); /* Return error code */
-
- /* Read the data in the track */
- error = MFE_OK;
- while (tklen > 0 && error == MFE_OK)
- error = read_event(&tklen);
- if (error != MFE_OK)
- return(error);
-
- if (!eot_called) /* Has 'Mf_eot()' been called? */
- bailout(MFE_NOEOT)
-
- return(MFE_OK); /* All went well */
- }
-
- X/* MIDIFILE
- * This is the main routine of the whole thing, and the only one which
- * should be accessible to the user. It reads in a MIDI format file,
- * parses it, and passes the various parts on to user-supplied routines.
- * Although it is very similar to Tim Thompson's 'midifile()', and performs
- * the same function, it does _not_ cravenly exit the program as soon as
- * an error occurs, and therefore can safely be used inside another
- * application.
- * Since 'midifile()' does not care where the input file is coming from,
- * the user is expected to do all of the grunt work of opening files and
- * all that.
- * Returns MFE_OK if all goes well, or an error code if anything goes wrong,
- * including user disapproval.
- */
- int midifile()
- {
- int i;
- int ntrks;
-
- /* Initialize 'Mf_currtime' and 'Mf_deltatime' */
- Mf_currtime =
- Mf_deltatime = 0L;
-
- if (Mf_getc == NULL)
- bailout(MFE_NOGETC)
-
- /* Read the file header, and return an error code if
- * anything wrong occurs.
- */
- if ((ntrks = read_header()) < 0)
- return(ntrks); /* Return error value */
-
- /* Read the tracks, and return an error code if anything
- * wrong occurs.
- */
- for (i = 0; i < ntrks; i++)
- {
- int error;
-
- if ((error = read_track()) != MFE_OK)
- return(error);
- }
-
- return(MFE_OK); /* No errors */
- }
- //go.sysin dd *
- if [ `wc -c < midifile/mf.c` != 19018 ]; then
- made=FALSE
- /bin/echo 'error transmitting "midifile/mf.c" --'
- /bin/echo 'length should be 19018, not' `wc -c < midifile/mf.c`
- else
- made=TRUE
- fi
- if [ $made = TRUE ]; then
- /bin/chmod 664 midifile/mf.c
- /bin/echo -n ' '; /bin/ls -ld midifile/mf.c
- fi
- /bin/echo 'Extracting midifile/mf.h'
- sed 's/^X//' <<'//go.sysin dd *' >midifile/mf.h
- X/* MF.H
- * Definitions etc. for MIDI file analyzer.
- */
-
- int (*Mf_getc)(); /* Returns -1 on EOF */
- int (*Mf_error)();
- int (*Mf_header)();
- int (*Mf_starttrack)();
- int (*Mf_endtrack)();
- int (*Mf_on)();
- int (*Mf_off)();
- int (*Mf_pressure)();
- int (*Mf_controller)();
- int (*Mf_pitchbend)();
- int (*Mf_program)();
- int (*Mf_chanpressure)();
- int (*Mf_sysex)();
- int (*Mf_metamisc)();
- int (*Mf_sqspecific)();
- int (*Mf_seqnum)();
- int (*Mf_text)();
- int (*Mf_eot)();
- int (*Mf_timesig)();
- int (*Mf_smpte)();
- int (*Mf_tempo)();
- int (*Mf_keysig)();
- int (*Mf_arbitrary)();
-
- X/* ERROR CODES
- * These are the error codes returned by the various routines.
- */
- #define MFE_OK 0 /* No error */
- #define MFE_EOF -1 /* Premature end of file */
- #define MFE_HKWEXP -2 /* Header keyword expected */
- #define MFE_NOGETC -3 /* 'Mf_getc' is not defined */
- #define MFE_ILLHLEN -4 /* Illegal header length */
- #define MFE_USER -5 /* User disapproves of something */
- #define MFE_ILLFORM -6 /* Illegal file format */
- #define MFE_TKWEXP -7 /* Track keyword expected */
- #define MFE_ILLSETYPE -8 /* Illegal SysEx message type */
- #define MFE_MEM -9 /* Insufficient memory */
- #define MFE_ILLSTAT -10 /* Illegal status byte */
- #define MFE_ILLMLEN -11 /* Illegal meta-event length */
- #define MFE_ILLMVAL -12 /* Illegal value in meta-event */
- #define MFE_UKMETA -13 /* Unknown meta-event */
- #define MFE_NOEOT -14 /* No end-of-track meta-event */
-
- X/* ERR_MSGS
- * Error messages corresponding to the various errors that might occur.
- */
- char *err_msgs[] = {
- "No error",
- "Premature end of file",
- "'MThd' expected",
- "'Mf_getc' undefined",
- "Illegal header length",
- "User disapproval",
- "Illegal file format",
- "'MTrk' expected",
- "Illegal system-exclusive message type",
- "Insufficient memory",
- "Illegal status byte",
- "Illegal meta-event length",
- "Illegal value in meta-event",
- "Unknown meta-event",
- "Missing end-of-track meta-event",
- NULL
- };
-
- X/* BAILOUT
- * Call 'Mf_error()' with the error code 'code', then return that code.
- */
- #define bailout(code) {if(Mf_error!=NULL)(*Mf_error)(err_msgs[-(code)]);\
- return(code);}
-
- X/* MIDI COMMANDS */
- #define MIDI_NOTEOFF 0x80 /* Note off */
- #define MIDI_NOTEON 0x90 /* Note on */
- #define MIDI_PRESSURE 0xa0 /* Polyphonic key pressure */
- #define MIDI_CONTROL 0xb0 /* Control change */
- #define MIDI_PROGRAM 0xc0 /* Program change */
- #define MIDI_CHANPRES 0xd0 /* Channel pressure */
- #define MIDI_PITCHB 0xe0 /* Pitch wheel change */
- #define MIDI_SYSEX 0xf0 /* System exclusive data */
-
- X/* META-EVENT MESSAGE TYPES */
- #define META_SEQNUM 0x00 /* Sequence number */
- #define META_TEXT 0x01 /* Text event */
- #define META_COPYRIGHT 0x02 /* Copyright notice */
- #define META_SEQNAME 0x03 /* Sequence/track name */
- #define META_INSTNAME 0x04 /* Instrument name */
- #define META_LYRIC 0x05 /* Lyric */
- #define META_MARKER 0x06 /* Marker */
- #define META_CUEPT 0x07 /* Cue point */
- #define META_EOT 0x2f /* End of track */
- #define META_TEMPO 0x51 /* Set tempo */
- #define META_SMPTE 0x54 /* SMPTE offset */
- #define META_TIMESIG 0x58 /* Time signature */
- #define META_KEYSIG 0x59 /* Key signature */
- #define META_SEQSPEC 0x7f /* Sequencer-specific event */
- //go.sysin dd *
- if [ `wc -c < midifile/mf.h` != 3174 ]; then
- made=FALSE
- /bin/echo 'error transmitting "midifile/mf.h" --'
- /bin/echo 'length should be 3174, not' `wc -c < midifile/mf.h`
- else
- made=TRUE
- fi
- if [ $made = TRUE ]; then
- /bin/chmod 664 midifile/mf.h
- /bin/echo -n ' '; /bin/ls -ld midifile/mf.h
- fi
- /bin/echo 'Extracting midifile/midifile.3'
- sed 's/^X//' <<'//go.sysin dd *' >midifile/midifile.3
- X.TH MIDIFILE.3 "May 12, 1990"
- X.AT 3
- X.SH NAME
- midifile \- a MIDI file parser
- X.SH SYNOPSIS
- X.B #include
- "midifile.h"
- X.PP
- X.B int midifile()
- X.PP
- X.B int (*Mf_getc)()
- X.PP
- X.B int (*Mf_error)(str)
- X.br
- X.B char *str;
- X.PP
- X.B int (*Mf_header)(format,ntrks,division)
- X.br
- X.B short format, ntrks, division;
- X.PP
- X.B int (*Mf_starttrack)()
- X.PP
- X.B int (*Mf_on)(chan,note,vol)
- X.br
- X.B unsigned char chan, note, vol;
- X.PP
- X.B int (*Mf_off)(chan,note,vol)
- X.br
- X.B unsigned char chan, note, vol;
- X.PP
- X.B int (*Mf_pressure)(chan,note,pressure)
- X.br
- X.B unsigned char chan, note, pressure;
- X.PP
- X.B int (*Mf_controller)(chan,control,value)
- X.br
- X.B unsigned char chan, control, value;
- X.PP
- X.B int (*Mf_pitchbend)(chan,lsb,msb)
- X.br
- X.B unsigned char chan, lsb, msb;
- X.PP
- X.B int (*Mf_program)(chan,prog)
- X.br
- X.B unsigned char chan, prog;
- X.PP
- X.B int (*Mf_chanpressure)(chan,pressure)
- X.br
- X.B unsigned char chan, pressure;
- X.PP
- X.B int (*Mf_sysex)()
- X.PP
- X.B int (*Mf_metamisc)()
- X.PP
- X.B int (*Mf_sqspecific)(chan,length,data)
- X.br
- X.B unsigned char chan;
- X.br
- X.B long length;
- X.br
- X.B char *data;
- X.PP
- X.B int (*Mf_seqnum)(seqnum)
- X.br
- X.B short seqnum
- X.PP
- X.B int (*Mf_text)(msg_type,length,data)
- X.br
- X.B int msg_type;
- X.br
- X.B long length;
- X.br
- X.B char *data;
- X.PP
- X.B int (*Mf_eot)()
- X.PP
- X.B int (*Mf_timesig)(numer,denom,clocks,qnotes)
- X.br
- X.B int numer, denom, clocks, qnotes;
- X.PP
- X.B int (*Mf_smpte)(hour,min,sec,frame,fract)
- X.br
- X.B int hour, min, sec, frame, fract;
- X.PP
- X.B int (*Mf_tempo)(tempo)
- X.br
- X.B unsigned long tempo;
- X.PP
- X.B int (*Mf_keysig)(sharpflat,minor)
- X.br
- X.B int sharpflat, minor;
- X.PP
- X.B int (*Mf_arbitrary)()
- X.PP
- X.B long Mf_currtime
- X.PP
- X.B long Mf_deltatime
- X.SH DESCRIPTION
- X.I midifile
- is a routine that parses an arbitrary MIDI format file.
- It contains hooks for every type of event, for the user
- to add his own event-handling functions. Each time
- X.I midifile
- reads an event, it calls a user function (if one is
- defined, i.e., if it is non-NULL) with a description of the event.
- X.PP
- Not all functions must be supplied at all times:
- X.I midifile
- always checks for the existence of a user function
- before calling it. Thus, if you're only interested in the
- third track, you can have
- X.B Mf_starttrack
- assign values to the appropriate functions at the beginning of the
- third track, and
- X.B Mf_eot
- reset them to NULL.
- X.PP
- The only function which must be supplied is
- X.I Mf_getc,
- which should read a byte from the MIDI file and return it; it
- should return -1 in case of end of file, and a negative error
- code in case of error.
- X.PP
- X.I Mf_error
- is called in case of error. It is passed a string describing
- the error. If
- X.I Mf_error
- returns a negative value, then
- X.I midifile
- aborts and returns that value.
- X.PP
- Actually, any user function can abort the execution of
- X.I midifile:
- anytime a function returns a negative value,
- X.I midifile
- returns that value to the calling routine.
- X.PP
- X.I Mf_header
- is called whene the file header has been read. Its parameters are
- the file format (0, 1 or 2), the number of tracks in the file,
- and the division of quarter-notes, as described by the deltas in
- the file.
- X.PP
- X.I Mf_starttrack
- is called at the beginning of each track.
- X.PP
- X.I Mf_on
- and
- X.I Mf_off
- are called each time
- X.I midifile
- reads a MIDI ON and MIDI OFF event, respectively. The parameters
- are the MIDI channel, the note number, and the note-on (or off)
- velocity.
- X.PP
- X.I Mf_pressure
- is called for polyphonic key pressure (after-touch) events. It
- gives the channel, the note number, and the pressure value.
- Similarly,
- X.I Mf_chanpressure
- is called for channel pressure events.
- X.PP
- X.I Mf_controller
- is called for controller changes. It gives the MIDI channel, the
- controller number, and the controller value.
- X.PP
- X.I Mf_pitchbend
- is called for pitch wheel events. It gives the MIDI channel, the
- least-significant and most-significant bytes of the pitch-bend
- value.
- X.PP
- X.I Mf_program
- is called for program (patch) changes. It gives the MIDI channel
- and the program number.
- X.PP
- X.I Mf_sysex
- is called for system-exclusive messages. This function is not yet
- implemented.
- X.PP
- X.I midifile
- includes a series of functions designed to deal with the various
- meta-events described by the MIDI 1.0 file spec. In each case,
- the
- X.I length
- parameter, when given, includes the end-of-system-exclusive marker.
- X.PP
- X.I Mf_metamisc
- is called for miscellaneous meta-events, as described in the
- MIDI 1.0 file spec. This function is not yet implemented.
- X.PP
- X.I Mf_sqspecific
- is called for sequencer-specific meta-events. It gives the MIDI
- channel number, the length of the event, and a data string
- containing the data.
- X.PP
- X.I Mf_seqnum
- specifies the number of a sequence.
- X.PP
- X.I Mf_text
- covers the entire class of text meta-events. It gives the
- message type (miscellaneous, copyright notice, track name, etc.),
- the text length, and a string containing the text.
- X.PP
- X.I Mf_eot
- is called for the end-of-track meta-event.
- X.PP
- X.I Mf_timesig
- gives the time signature of a piece. The time signature, as given
- at the beginning of a piece is given by
- X.B numer/(2^-denom).
- X.B clocks
- represents the number of MIDI clocks per metronome click.
- X.B qnotes
- gives the number of notated 32nd-notes in a MIDI quarter-note.
- X.PP
- X.I Mf_smpte
- specifies a SMPTE offset for a track. It gives the number of
- hours, minutes, seconds, frames and 100ths of a frame.
- X.PP
- X.I Mf_tempo
- sets the tempo, in microseconds per MIDI quarter-note.
- X.PP
- X.I Mf_keysig
- gives the key signature of the piece.
- X.B sharpflat
- gives the number of sharps or flats in the signature: 0 means
- the piece is in the key of C, -1 means there is one flat in
- the signature, 1 means there is one sharp, and so forth.
- X.I minor
- is set to 0 if the piece is in a major key, and 1 if it is in
- a minor key.
- X.PP
- There are two user-accessible variables used by
- X.I midifile:
- X.I Mf_currtime
- and
- X.I Mf_deltatime
- X.I Mf_currtime
- measures the time elapsed between the beginning of the track
- and the current event.
- X.I Mf_deltatime
- is the event's delta, i.e., it measures the time elapsed between
- the previous event and the current event (or the beginning of
- the track, if the current event is the first one).
- X.SH FILES
- X.B midifile.h
- X.SH BUGS
- X.I midifile
- does not handle system-exclusive messages, except for the
- meta-events described in the
- X.I MIDI 1.0 File Specification.
- X.SH AUTHOR
- Original author: Tim Thompson. This version was rewritten and
- enhanced by Andrew Arensburger.
- X.SH "SEE ALSO"
- X.IR "MIDI 1.0 specification"
- X.br
- X.I "MIDI 1.0 file specification"
- //go.sysin dd *
- if [ `wc -c < midifile/midifile.3` != 6394 ]; then
- made=FALSE
- /bin/echo 'error transmitting "midifile/midifile.3" --'
- /bin/echo 'length should be 6394, not' `wc -c < midifile/midifile.3`
- else
- made=TRUE
- fi
- if [ $made = TRUE ]; then
- /bin/chmod 664 midifile/midifile.3
- /bin/echo -n ' '; /bin/ls -ld midifile/midifile.3
- fi
- /bin/echo 'Extracting midifile/midifile.h'
- sed 's/^X//' <<'//go.sysin dd *' >midifile/midifile.h
- X/* MIDI.H
- * Definitions etc. for MIDI file analyzer.
- */
-
- extern int (*Mf_getc)();
- extern int (*Mf_error)();
- extern int (*Mf_header)();
- extern int (*Mf_starttrack)();
- extern int (*Mf_endtrack)();
- extern int (*Mf_on)();
- extern int (*Mf_off)();
- extern int (*Mf_pressure)();
- extern int (*Mf_controller)();
- extern int (*Mf_pitchbend)();
- extern int (*Mf_program)();
- extern int (*Mf_chanpressure)();
- extern int (*Mf_sysex)();
- extern int (*Mf_metamisc)();
- extern int (*Mf_sqspecific)();
- extern int (*Mf_seqnum)();
- extern int (*Mf_text)();
- extern int (*Mf_eot)();
- extern int (*Mf_timesig)();
- extern int (*Mf_smpte)();
- extern int (*Mf_tempo)();
- extern int (*Mf_keysig)();
- extern int (*Mf_arbitrary)();
-
- extern int midifile();
-
- extern long Mf_currtime;
- extern long Mf_deltatime;
-
- X/* ERROR CODES
- * These are the error codes returned by 'midifile()'.
- */
- #define MFE_OK 0 /* No error */
- #define MFE_EOF -1 /* Premature end of file */
- #define MFE_HKWEXP -2 /* Header keyword expected */
- #define MFE_NOGETC -3 /* 'Mf_getc' is not defined */
- #define MFE_ILLHLEN -4 /* Illegal header length */
- #define MFE_USER -5 /* User disapproves of something */
- #define MFE_ILLFORM -6 /* Illegal file format */
- #define MFE_TKWEXP -7 /* Track keyword expected */
- #define MFE_ILLSETYPE -8 /* Illegal SysEx message type */
- #define MFE_MEM -9 /* Insufficient memory */
- #define MFE_ILLSTAT -10 /* Illegal status byte */
- #define MFE_ILLMLEN -11 /* Illegal meta-event length */
- #define MFE_ILLMVAL -12 /* Illegal value in meta-event */
- #define MFE_UKMETA -13 /* Unknown meta-event */
- #define MFE_NOEOT -14 /* No end-of-track meta-event */
-
- //go.sysin dd *
- if [ `wc -c < midifile/midifile.h` != 1621 ]; then
- made=FALSE
- /bin/echo 'error transmitting "midifile/midifile.h" --'
- /bin/echo 'length should be 1621, not' `wc -c < midifile/midifile.h`
- else
- made=TRUE
- fi
- if [ $made = TRUE ]; then
- /bin/chmod 664 midifile/midifile.h
- /bin/echo -n ' '; /bin/ls -ld midifile/midifile.h
- fi
- made=TRUE
- if [ $made = TRUE ]; then
- /bin/chmod 775 midifile
- /bin/echo -n ' '; /bin/ls -ld midifile
- fi
-
- exit 0 # Just in case...
- --
- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
- Sterling Software, IMD UUCP: uunet!sparky!kent
- Phone: (402) 291-8300 FAX: (402) 291-4362
- Please send comp.sources.misc-related mail to kent@uunet.uu.net.
-