Submitted-by: Oishii Ichigo <whiz@well.sf.ca.us>
Posting-number: Volume 21, Issue 96
Archive-name: midilib/part01
Environment: UNIX, MIDI, Sun, Mac, PC

This is submission contains a library of code to read 
and write standard MIDI files, a standard as defined by the MMA, the 
MIDI Manufacturer's Assocation. The format spec is available from the IMA:

                International MIDI Association
                5316 West 57th Street
                Los Angeles, CA 90056

"make mftext" will compile a program that gives a verbose textual
listing of a MIDI file, and is an example of how to read a file.  "make
mf1to0" will compile a program that demonstrates how to write midi
files, and converts format 1 multitrack files to format 0.  "make
midifile.man" will produce a formatted manual page.  Example MIDI files
are named "example*.uu".  They're uuencoded; "make uutomid" will
uudecode them all.  The first two examples (example1 and example2) are
the examples given in the 0.06 version of the standard MIDI file
specification.

This library was written on UNIX, and tested on Sun's, PC's and Mac's.  
---
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  midifile midifile/example5.mid.uu midifile/mf1to0.c
#   midifile/midifile.c midifile/midifile.doc
# Wrapped by kent@sparky on Wed Aug  7 09:29:27 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test ! -d 'midifile' ; then
    echo shar: Creating directory \"'midifile'\"
    mkdir 'midifile'
fi
if test -f 'midifile/example5.mid.uu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/example5.mid.uu'\"
else
  echo shar: Extracting \"'midifile/example5.mid.uu'\" \(1431 characters\)
  sed "s/^X//" >'midifile/example5.mid.uu' <<'END_OF_FILE'
Xbegin 660 example5.mid
XM351H9     8    ! #!-5')K   #X@#_ 294<F%S:"$A " @(" @(" @(" @
XM(" @(" @(" @(" @(" @(" @( "0/D  L 0K  0N 00S @0W 00Z 00^ 01!
XM 01$ = " @4!L 1( =   + $30"0/@ !L 13 01: 01A 01G = # ; $; '0
XM$@"P!' !!'0!T#  L 1W 01Z =!3 + $?P&00$ !T'4"?P2P 5," 5 " 4P!
XM!'< D$   ; $< $!20 $:@$$9 $$7P$!1P$$6@$$50$$4 $$3 $!00 $1P$$
XM0@&00D  L 0\ 0$^  0W 00Q 0$Z  0K 00E @$U  0> 009 0$Q  03 >!\
XM/@"P! \!X'L] + $"@'@>#P L 0$ 0$L  0  )!"  '@=SL =#H!L $H >!S
XM.0%P. !L-@%K-0"P 2,!X&@T 6<S &,Q 6 P + !' '@7"X!6"P 5RL!5"H 
XML $7 9!#0 #@4RD!4"@!3R< L $3 00# 00( 00, 000 006 0$8  0< >!3
XM*0%7*P"P!"(! 1T !"@!X%LM + $+P"00P !X& P + $-P$!) #@9#( L 0^
XM >!H- "P!$0!X&PV + !+  $2@'@<#@ L 11 >!T.@"P!%8!X'@\ + !,P $
XM70'@?#X L 1C >  0 "P!&H! 3H!!&\!D$5  + !/@ $<@$$= $!0@ $=P$$
XM>@$$?0$!10 $?P(!2 (!3 (!40&010 !L %4 @%8 @%< P%A > $0@$(1 $,
XM1@&01T  L %J  1_ > 02  42@"P!'@!!'(! 7  X!M- + $; +@'T\ L %T
XM  1H 01E > C40"P!&0!X"=3 + $8@$!>  $70'@*U4 L 16 01. > O5P"P
XM!$8 D$<  ; !?  $/@$$. $$- $$, '@+%8 L 0M > K50"P 7D!X"=3 + $
XM*0'@(U$ 'T\ L 0E > ;30"P 6X !"$!X!1* + $' '@$$@ "T4 L 08 9!)
XM0 #@!T, L %@  03 >  0 "P! X!! H! 5  ! 8!! (! 44 !  " 3P" 38#
XM 3( D$D  K !+P$$ @$!*P $!@$$"P'@?#X!>ST L $E  00 >!W.P!S.0"P
XM!!8!X&\W + $'0'@:S4 L $9  0D >!D,@!C,0"P!"L!D$I  .!<+@"P!#(!
XMX%LM + !#0 $.@'@5RL L 1" >!3*0!0* "P!$<!X$\G + ! @ $2@+@2R4!
XML $  .!() 602@ "X$PF 5 H ; !"0#@5RL!8# !:S4!<#@ L $A >![/0  
X:0 *P 3, T"@"L $_ =   K !1P(!2FS_+P !
X 
Xend
END_OF_FILE
  if test 1431 -ne `wc -c <'midifile/example5.mid.uu'`; then
    echo shar: \"'midifile/example5.mid.uu'\" unpacked with wrong size!
  fi
  # end of 'midifile/example5.mid.uu'
fi
if test -f 'midifile/mf1to0.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/mf1to0.c'\"
else
  echo shar: Extracting \"'midifile/mf1to0.c'\" \(14176 characters\)
  sed "s/^X//" >'midifile/mf1to0.c' <<'END_OF_FILE'
X/*
X * f1to0.c 4/30/89
X *
X * Author:  Michael S. Czeiszperger
X * Purpose: The program converts format 1 MIDI files to format 0
X *          MIDI files.  It is meant to demonstrate the use of
X *          the midifile library.  The internal data format is
X *          a linked list of MIDI and SMF meta events.
X *
X *          The spec is available from:
X *               International MIDI Association
X *               5316 West 57th Street
X *               Los Angeles, CA 90056
X *
X *          An in-depth description of the spec can also be found
X *          in the article "Introducing Standard MIDI Files", published
X *          in Electronic Musician magazine, April, 1988.
X * 
X * usage: f1to0 infile outfile
X * 
X */
X 
X#include <stdio.h>
X#include <ctype.h>
X
X#ifdef THINK
X#include <stdlib.h>
X#else
X#include <malloc.h>
X#endif
X
X#include "midifile.h"
X
Xenum PACKET_TYPE { MIDI_EVENT, META_EVENT };
X
Xstruct MIDIpacket	    /* Stucture for MIDI events */
X{ 							
X    enum PACKET_TYPE p_type; /* What type of packet is it?               */
X    unsigned long time;      /* absolute time of event, in ms increments */
X    unsigned int m_type;     /* What kind of MIDI message?               */
X    unsigned int chan;       /* The MIDI channel                         */
X    unsigned long len;       /* length of the midi message               */
X    unsigned char *data;     /* pointer to the data                      */
X    struct MIDIpacket *next,*before;           /* point to next packet */
X} *sequence_start,*sequence_end;
X
X/* 
X   This example structure is not very portable. It does, however,
X   mirror the logical operation of the header, and on some machines can
X   be written and read directly.  It assumes that shorts are at least
X   two bytes and chars are at least one byte.
X*/
Xstruct header_type {
X	short format;
X	short ntrks;
X	union {
X		short quarter_note;
X		struct{
X			char format;
X			char resolution;
X    		} smpte;
X    	} division;
X} header;
X
X/* These lines are needed to use the library */
XFILE *fpIn,*fpOut;
Xextern long Mf_currtime;
Xmygetc() { return(getc(fpIn)); }
Xmyputc(c) { return(putc(c,fpOut));}
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X/* This is included of an example of what to do if you use THINK C on
X   the mac. */
X#ifdef THINK
X    #include <console.h>
X	    argc = ccommand(&argv);
X#endif
X
X    if((fpIn = fopen(argv[1],"r")) == 0L)
X	printf("f1to0: unable to open file %s for reading.\n",argv[1]);
X
X    if((fpOut = fopen(argv[2],"w")) == 0L)
X	printf("f1to0: unable to open file %s for writing.\n",argv[2]);
X
X    init_funcs();
X
X    /* read the midi file */
X    mfread();
X
X    /* write a single track */
X    mfwrite((int)header.format,1,(int)header.division.quarter_note,fpOut);
X}
X
Xmyheader(format,ntrks,division)
Xint format,ntrks,division;
X{
X    header.format = format;
X    header.ntrks = ntrks;
X    header.division.quarter_note = division;
X}
X
Xmyerror(s)
Xchar *s;
X{
X	printf("%10ld: %s\n",Mf_currtime,s);
X}
X
X/*
X * mywritetrack()
X *
X * Sample showing how to use the library routines to write out a track.
X * Returns 1 if successful, and -1 if not.
X *
X */
Xint mywritetrack(track)
Xint track;
X{
X    struct MIDIpacket *current;
X    unsigned long delta_time;
X
X    current = sequence_start; /* init to point at the beginning of the list */
X
X    /* shuffle through each element in the linked list, writing out
X       the data in the standard MIDI format */
X    while(current != 0L)
X    {
X        if(current == sequence_start)
X           delta_time = current->time;
X        else
X           delta_time = current->time - (current->before)->time;
X#ifdef DEBUG
Xprintf("ticks = %lu event = %d packet = %d chan = %d len = %lu\n",
Xcurrent->time,current->m_type,current->p_type,current->chan,current->len);
X#endif
X
X	if(current->p_type == MIDI_EVENT)
X	{
X	    if(mf_write_midi_event(delta_time,current->m_type,current->chan,current->data,current->len) < 0)
X	        return(-1);
X	}
X	else if(current->p_type == META_EVENT)
X	{
X	    if(mf_write_meta_event(delta_time,current->m_type,current->data,current->len) < 0)
X	        return(-1);
X	}
X	else
X	    fprintf(stderr,"Unknown MIDI packet encounted.\n");
X
X        current = current->next;
X    }
X    return(1);
X} /* end of write_track() */
X
X
X/*
X * This routine adds a MIDI or META event into the linked list.  
X */
Xvoid add_packet(ticks,event_type,packet_type,chan,data,len)
Xunsigned long ticks;                 /* absolute ticks of event */
Xint event_type;                      /* note_on, note_off, etc  */
Xenum PACKET_TYPE packet_type;        /* What kind of data?      */
Xunsigned char *data;                 /* already allocated data  */
Xunsigned long len;                   /* length of data          */
X{
X    struct MIDIpacket *scratch,*insert_pt,*get_insert_point();
X
X#ifdef DEBUG
Xprintf("ticks = %d event = %d packet = %d chan = %d len = %d\n",ticks,event_type,packet_type,chan,len);
X#endif
X    scratch = (struct MIDIpacket *) malloc(sizeof(struct MIDIpacket));
X
X    if(scratch == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X
X    scratch->data = data;
X    scratch->m_type = event_type;
X    scratch->p_type = packet_type;
X    scratch->chan = chan;
X    scratch->len  = len;
X    scratch->time = ticks;
X    
X    /* Where in the linked list should this packet go? */
X    insert_pt = get_insert_point(sequence_start,sequence_end,ticks);
X   
X    if(sequence_start == 0L)       /* the list is empty */
X    {  
X    	sequence_start = sequence_end = scratch;
X        scratch->before = 0L;
X        scratch->next = 0L;
X    }
X    else if(insert_pt == 0L) /* insert the new bottom record */
X    { 
X        sequence_end->next = scratch;
X        scratch->before = sequence_end;
X        scratch->next = 0L;
X        sequence_end = scratch;
X    }
X    else if(insert_pt == sequence_start) /* insert the new top record */
X    {      
X    	sequence_start->before = scratch;
X        scratch->before = 0L;
X        scratch->next = sequence_start;
X        sequence_start = scratch;
X    }
X    else  /* insert before the insert pointer */
X    {      
X    	scratch->next = insert_pt;
X        scratch->before = insert_pt->before;
X        insert_pt->before = scratch;
X        (scratch->before)->next = scratch;
X    }
X} /* end of add_packet() */
X
X/*
X * get_insert_point()
X *    A routine to help insertion into a linked list,  
X *    by returning a pointer to the position in the list
X *    where the MIDI packet should be inserted based on
X *    the time the MIDI event occurs. This could be a lot
X *    faster, but was left simple for easy debugging.
X *    If you'd like to speed this up, please go ahead!
X */
Xstruct MIDIpacket *get_insert_point(top,bottom,time)
Xstruct MIDIpacket *top,*bottom;
Xunsigned long time;
X{
X    struct MIDIpacket *index;
X    /* this keeps track of the last inset point, making it easier */
X    /* to find the next one assuming that most times will be     */
X    /* consecutive.                                               */
X    static struct MIDIpacket *insert_pointer;
X	
X    if(top == 0L || bottom == 0L) return(0L);
X    if(time > bottom->time) return(0L); /* tack onto end of list */
X    if(time < top->time) return(top); /* insert as the new first record */	
X
X    /* check around where the last packet was inserted */
X    if(insert_pointer != 0L)
X        index = insert_pointer;
X    else
X    {
X        index = sequence_start;
X        insert_pointer = index;
X    }
X    if(time > index->time)
X    {
X        while(time > index->time && index != 0L)
X	    index = index->next;
X        return(index);
X    }
X    else if(time < index->time)
X    {
X        while(time < index->time && index != 0L)
X	    index = index->before;
X        return(index->next);
X    }
X    else 
X	return(insert_pointer);
X} /* end of get_insert_point() */
X
X/* Routines called from midifile lib */
Xmynoteon(chan,c1,c2)
Xint chan,c1,c2;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 2);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) c1;
X    data[1] = (unsigned char) c2;
X    add_packet((unsigned long)Mf_currtime,note_on,MIDI_EVENT,chan,data,(unsigned long)2);
X}
X
Xmynoteoff(chan,c1,c2)
Xint chan,c1,c2;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 2);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) c1;
X    data[1] = (unsigned char) c2;
X    add_packet((unsigned long)Mf_currtime,note_off,MIDI_EVENT,chan,data,(unsigned long)2);
X}
X
Xmypressure(chan,pitch,pressure)
Xint chan,pitch,pressure;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 2);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) pitch;
X    data[1] = (unsigned char) pressure;
X    add_packet((unsigned long)Mf_currtime,poly_aftertouch,MIDI_EVENT,chan,data,(unsigned long)2);
X}
X
Xmykeypressure(chan,pitch,pressure)
Xint chan,pitch,pressure;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 2);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) pitch;
X    data[1] = (unsigned char) pressure;
X    add_packet((unsigned long)Mf_currtime,channel_aftertouch,MIDI_EVENT,chan,data,(unsigned long)2);
X}
X
Xmyparameter(chan,control,value)
Xint chan,control,value;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 2);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) control;
X    data[1] = (unsigned char) value;
X    add_packet((unsigned long)Mf_currtime,control_change,MIDI_EVENT,chan,data,(unsigned long)2);
X}
X
Xmytempo(microsecs)
Xunsigned long microsecs;
X{
X    unsigned char *data;
X    data = (unsigned char *) malloc(sizeof(char) * 3);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[2] = (unsigned)(microsecs & 0xff);
X    data[1] = (unsigned)((microsecs >> 8) & 0xff);
X    data[0] = (unsigned)((microsecs >> 16) & 0xff);
X    add_packet((unsigned long)Mf_currtime,set_tempo,META_EVENT,0,data,(unsigned long)3);
X}
X
Xmyvarlen(type,len,msg)
Xint type,len;
Xchar *msg;
X{
X    unsigned char *data;
X    int i;
X
X    data = (unsigned char *) malloc((unsigned)(sizeof(char) * len));
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    for(i = 0; i < len; i++)
X        data[i] = msg[i];
X    add_packet((unsigned long)Mf_currtime,type,META_EVENT,0,data,(unsigned long)len);
X}
X
Xmysmpte(hour,min,sec,frame,fract)
Xchar hour,min,sec,frame,fract;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 5);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = hour;
X    data[1] = min;
X    data[2] = sec;
X    data[3] = frame;
X    data[4] = fract;
X    add_packet((unsigned long)Mf_currtime,smpte_offset,META_EVENT,0,data,(unsigned long)5);
X}
X
Xmyprogram(chan,program)
Xint chan, program;
X{
X    unsigned char *data;
X    data = (unsigned char *) malloc(sizeof(char) * 1);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) program;
X    add_packet((unsigned long)Mf_currtime,program_chng,MIDI_EVENT,chan,data,(unsigned long)1);
X}
X
Xmypitchbend(chan,msb,lsb)
Xint chan, msb,lsb;
X{
X    unsigned char *data;
X    data = (unsigned char *) malloc(sizeof(char) * 2);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) msb;
X    data[1] = (unsigned char) lsb;
X    add_packet((unsigned long)Mf_currtime,pitch_wheel,MIDI_EVENT,chan,data,(unsigned long)2);
X}
X
Xmysysex(len,msg)
Xint len;
Xchar *msg;
X{
X    unsigned char *data;
X    int i;
X
X    data = (unsigned char *) malloc((unsigned)(sizeof(char) * len));
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    for(i = 0; i < len; i++)
X        data[i] = msg[i];
X    add_packet((unsigned long)Mf_currtime,system_exclusive,MIDI_EVENT,0,data,(unsigned long)len);
X}
X
Xmyseqnum(num)
Xint num;
X{
X    unsigned char *data;
X    data = (unsigned char *) malloc(sizeof(char) * 1);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = (unsigned char) num;
X    add_packet((unsigned long)Mf_currtime,sequence_number,META_EVENT,0,data,(unsigned long)1);
X}
X
Xmytimesig(numer,denom,clocks,qnotes)
Xchar numer,denom,clocks,qnotes;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 4);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = numer;
X    data[1] = denom;
X    data[2] = clocks;
X    data[3] = qnotes;
X    add_packet((unsigned long)Mf_currtime,time_signature,META_EVENT,0,data,(unsigned long)4);
X}
X
Xmykeysig(sharpflat, minor)
Xint sharpflat, minor;
X{
X    unsigned char *data;
X
X    data = (unsigned char *) malloc(sizeof(char) * 2);
X    if(data == NULL)
X    {
X          fprintf(stderr,"Sorry, out of memory!\n");
X          exit(1);
X    }
X    data[0] = sharpflat;
X    data[1] = minor;
X    add_packet((unsigned long)Mf_currtime,key_signature,META_EVENT,0,data,(unsigned long)2);
X}
X
Xinit_funcs()
X{
X    sequence_start = 0L;
X    sequence_end = 0L;
X
X    /* needed for reading */
X    Mf_getc = mygetc;
X    Mf_error = myerror;
X    Mf_header = myheader;
X    Mf_noteon = mynoteon;
X    Mf_noteoff = mynoteoff;
X    Mf_pressure = mypressure;
X    Mf_parameter = myparameter;
X    Mf_pitchbend = mypitchbend;
X    Mf_program = myprogram;
X    Mf_chanpressure = mykeypressure;
X    Mf_sysex = mysysex;
X    Mf_metamisc = myvarlen;    
X    Mf_seqspecific = myvarlen;    
X    Mf_seqnum =  myseqnum;
X    Mf_text = myvarlen;
X    Mf_timesig = mytimesig;
X    Mf_smpte = mysmpte;
X    Mf_tempo = mytempo;
X    Mf_keysig = mykeysig;
X
X   /* needed for writing */
X    Mf_putc = myputc;
X    Mf_writetrack = mywritetrack;
X}
END_OF_FILE
  if test 14176 -ne `wc -c <'midifile/mf1to0.c'`; then
    echo shar: \"'midifile/mf1to0.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/mf1to0.c'
fi
if test -f 'midifile/midifile.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/midifile.c'\"
else
  echo shar: Extracting \"'midifile/midifile.c'\" \(19054 characters\)
  sed "s/^X//" >'midifile/midifile.c' <<'END_OF_FILE'
X/*
X * midifile 1.11
X * 
X * Read and write a MIDI file.  Externally-assigned function pointers are 
X * called upon recognizing things in the file.
X *
X * Original release ?
X * June 1989 - Added writing capability, M. Czeiszperger.
X *
X *          The file format implemented here is called
X *          Standard MIDI Files, and is part of the Musical
X *          instrument Digital Interface specification.
X *          The spec is avaiable from:
X *
X *               International MIDI Association
X *               5316 West 57th Street
X *               Los Angeles, CA 90056
X *
X *          An in-depth description of the spec can also be found
X *          in the article "Introducing Standard MIDI Files", published
X *          in Electronic Musician magazine, April, 1989.
X * 
X */
X#include "midifile.h"
X#define NULLFUNC 0
X#define NULL 0
X
X#define THINK
X
X#ifdef THINK
X#include <stdlib.h>
X#endif
X
X#include <stdio.h>
X
Xchar *strcpy(), *strcat();
Xvoid exit(), free();
X
X/* public stuff */
X
X/* Functions to be called while processing the MIDI file. */
Xint (*Mf_getc)() = NULLFUNC;
Xint (*Mf_error)() = NULLFUNC;
Xint (*Mf_header)() = NULLFUNC;
Xint (*Mf_trackstart)() = NULLFUNC;
Xint (*Mf_trackend)() = NULLFUNC;
Xint (*Mf_noteon)() = NULLFUNC;
Xint (*Mf_noteoff)() = NULLFUNC;
Xint (*Mf_pressure)() = NULLFUNC;
Xint (*Mf_parameter)() = NULLFUNC;
Xint (*Mf_pitchbend)() = NULLFUNC;
Xint (*Mf_program)() = NULLFUNC;
Xint (*Mf_chanpressure)() = NULLFUNC;
Xint (*Mf_sysex)() = NULLFUNC;
Xint (*Mf_arbitrary)() = NULLFUNC;
Xint (*Mf_metamisc)() = NULLFUNC;
Xint (*Mf_seqnum)() = NULLFUNC;
Xint (*Mf_eot)() = NULLFUNC;
Xint (*Mf_smpte)() = NULLFUNC;
Xint (*Mf_tempo)() = NULLFUNC;
Xint (*Mf_timesig)() = NULLFUNC;
Xint (*Mf_keysig)() = NULLFUNC;
Xint (*Mf_seqspecific)() = NULLFUNC;
Xint (*Mf_text)() = NULLFUNC;
X
X/* Functions to implement in order to write a MIDI file */
Xint (*Mf_putc)() = NULLFUNC;
Xint (*Mf_writetrack)() = NULLFUNC;
Xint (*Mf_writetempotrack)() = NULLFUNC;
X
Xint Mf_nomerge = 0;		/* 1 => continue'ed system exclusives are */
X				            /* not collapsed. */
Xlong Mf_currtime = 0L;		/* current time in delta-time units */
X
X/* private stuff */
Xstatic long Mf_toberead = 0L;
Xstatic long Mf_numbyteswritten = 0L;
X
Xstatic long readvarinum();
Xstatic long read32bit();
Xstatic long to32bit();
Xstatic int read16bit();
Xstatic int to16bit();
Xstatic char *msg();
X
Xmfread()	 	/* The only non-static function in this file. */
X{
X	if ( Mf_getc == NULLFUNC )
X		mferror("mfread() called without setting Mf_getc"); 
X
X	readheader();
X	while ( readtrack() )
X		;
X}
X
X/* for backward compatibility with the original lib */
Xmidifile()
X{
X    mfread();
X}
X
Xstatic
Xreadmt(s)		/* read through the "MThd" or "MTrk" header string */
Xchar *s;
X{
X	int n = 0;
X	char *p = s;
X	int c;
X
X	while ( n++<4 && (c=(*Mf_getc)()) != EOF ) {
X		if ( c != *p++ ) {
X			char buff[32];
X			(void) strcpy(buff,"expecting ");
X			(void) strcat(buff,s);
X			mferror(buff);
X		}
X	}
X	return(c);
X}
X
Xstatic
Xegetc()			/* read a single character and abort on EOF */
X{
X	int c = (*Mf_getc)();
X
X	if ( c == EOF )
X		mferror("premature EOF");
X	Mf_toberead--;
X	return(c);
X}
X
Xstatic
Xreadheader()		/* read a header chunk */
X{
X	int format, ntrks, division;
X
X	if ( readmt("MThd") == EOF )
X		return;
X
X	Mf_toberead = read32bit();
X	format = read16bit();
X	ntrks = read16bit();
X	division = read16bit();
X
X	if ( Mf_header )
X		(*Mf_header)(format,ntrks,division);
X
X	/* flush any extra stuff, in case the length of header is not 6 */
X	while ( Mf_toberead > 0 )
X		(void) egetc();
X}
X
Xstatic
Xreadtrack()		 /* read a track chunk */
X{
X	/* This array is indexed by the high half of a status byte.  It's */
X	/* value is either the number of bytes needed (1 or 2) for a channel */
X	/* message, or 0 (meaning it's not  a channel message). */
X	static int chantype[] = {
X		0, 0, 0, 0, 0, 0, 0, 0,		/* 0x00 through 0x70 */
X		2, 2, 2, 2, 1, 1, 2, 0		/* 0x80 through 0xf0 */
X	};
X	long lookfor;
X	int c, c1, type;
X	int sysexcontinue = 0;	/* 1 if last message was an unfinished sysex */
X	int running = 0;	/* 1 when running status used */
X	int status = 0;		/* status value (e.g. 0x90==note-on) */
X	int needed;
X
X	if ( readmt("MTrk") == EOF )
X		return(0);
X
X	Mf_toberead = read32bit();
X	Mf_currtime = 0;
X
X	if ( Mf_trackstart )
X		(*Mf_trackstart)();
X
X	while ( Mf_toberead > 0 ) {
X
X		Mf_currtime += readvarinum();	/* delta time */
X
X		c = egetc();
X
X		if ( sysexcontinue && c != 0xf7 )
X			mferror("didn't find expected continuation of a sysex");
X
X		if ( (c & 0x80) == 0 ) {	 /* running status? */
X			if ( status == 0 )
X				mferror("unexpected running status");
X			running = 1;
X		}
X		else {
X			status = c;
X			running = 0;
X		}
X
X		needed = chantype[ (status>>4) & 0xf ];
X
X		if ( needed ) {		/* ie. is it a channel message? */
X
X			if ( running )
X				c1 = c;
X			else
X				c1 = egetc();
X			chanmessage( status, c1, (needed>1) ? egetc() : 0 );
X			continue;;
X		}
X
X		switch ( c ) {
X
X		case 0xff:			/* meta event */
X
X			type = egetc();
X			lookfor = Mf_toberead - readvarinum();
X			msginit();
X
X			while ( Mf_toberead > lookfor )
X				msgadd(egetc());
X
X			metaevent(type);
X			break;
X
X		case 0xf0:		/* start of system exclusive */
X
X			lookfor = Mf_toberead - readvarinum();
X			msginit();
X			msgadd(0xf0);
X
X			while ( Mf_toberead > lookfor )
X				msgadd(c=egetc());
X
X			if ( c==0xf7 || Mf_nomerge==0 )
X				sysex();
X			else
X				sysexcontinue = 1;  /* merge into next msg */
X			break;
X
X		case 0xf7:	/* sysex continuation or arbitrary stuff */
X
X			lookfor = Mf_toberead - readvarinum();
X
X			if ( ! sysexcontinue )
X				msginit();
X
X			while ( Mf_toberead > lookfor )
X				msgadd(c=egetc());
X
X			if ( ! sysexcontinue ) {
X				if ( Mf_arbitrary )
X					(*Mf_arbitrary)(msgleng(),msg());
X			}
X			else if ( c == 0xf7 ) {
X				sysex();
X				sysexcontinue = 0;
X			}
X			break;
X		default:
X			badbyte(c);
X			break;
X		}
X	}
X	if ( Mf_trackend )
X		(*Mf_trackend)();
X	return(1);
X}
X
Xstatic
Xbadbyte(c)
Xint c;
X{
X	char buff[32];
X
X	(void) sprintf(buff,"unexpected byte: 0x%02x",c);
X	mferror(buff);
X}
X
Xstatic
Xmetaevent(type)
X{
X	int leng = msgleng();
X	char *m = msg();
X
X	switch  ( type ) {
X	case 0x00:
X		if ( Mf_seqnum )
X			(*Mf_seqnum)(to16bit(m[0],m[1]));
X		break;
X	case 0x01:	/* Text event */
X	case 0x02:	/* Copyright notice */
X	case 0x03:	/* Sequence/Track name */
X	case 0x04:	/* Instrument name */
X	case 0x05:	/* Lyric */
X	case 0x06:	/* Marker */
X	case 0x07:	/* Cue point */
X	case 0x08:
X	case 0x09:
X	case 0x0a:
X	case 0x0b:
X	case 0x0c:
X	case 0x0d:
X	case 0x0e:
X	case 0x0f:
X		/* These are all text events */
X		if ( Mf_text )
X			(*Mf_text)(type,leng,m);
X		break;
X	case 0x2f:	/* End of Track */
X		if ( Mf_eot )
X			(*Mf_eot)();
X		break;
X	case 0x51:	/* Set tempo */
X		if ( Mf_tempo )
X			(*Mf_tempo)(to32bit(0,m[0],m[1],m[2]));
X		break;
X	case 0x54:
X		if ( Mf_smpte )
X			(*Mf_smpte)(m[0],m[1],m[2],m[3],m[4]);
X		break;
X	case 0x58:
X		if ( Mf_timesig )
X			(*Mf_timesig)(m[0],m[1],m[2],m[3]);
X		break;
X	case 0x59:
X		if ( Mf_keysig )
X			(*Mf_keysig)(m[0],m[1]);
X		break;
X	case 0x7f:
X		if ( Mf_seqspecific )
X			(*Mf_seqspecific)(leng,m);
X		break;
X	default:
X		if ( Mf_metamisc )
X			(*Mf_metamisc)(type,leng,m);
X	}
X}
X
Xstatic
Xsysex()
X{
X	if ( Mf_sysex )
X		(*Mf_sysex)(msgleng(),msg());
X}
X
Xstatic
Xchanmessage(status,c1,c2)
Xint status;
Xint c1, c2;
X{
X	int chan = status & 0xf;
X
X	switch ( status & 0xf0 ) {
X	case 0x80:
X		if ( Mf_noteoff )
X			(*Mf_noteoff)(chan,c1,c2);
X		break;
X	case 0x90:
X		if ( Mf_noteon )
X			(*Mf_noteon)(chan,c1,c2);
X		break;
X	case 0xa0:
X		if ( Mf_pressure )
X			(*Mf_pressure)(chan,c1,c2);
X		break;
X	case 0xb0:
X		if ( Mf_parameter )
X			(*Mf_parameter)(chan,c1,c2);
X		break;
X	case 0xe0:
X		if ( Mf_pitchbend )
X			(*Mf_pitchbend)(chan,c1,c2);
X		break;
X	case 0xc0:
X		if ( Mf_program )
X			(*Mf_program)(chan,c1);
X		break;
X	case 0xd0:
X		if ( Mf_chanpressure )
X			(*Mf_chanpressure)(chan,c1);
X		break;
X	}
X}
X
X/* readvarinum - read a varying-length number, and return the */
X/* number of characters it took. */
X
Xstatic long
Xreadvarinum()
X{
X	long value;
X	int c;
X
X	c = egetc();
X	value = c;
X	if ( c & 0x80 ) {
X		value &= 0x7f;
X		do {
X			c = egetc();
X			value = (value << 7) + (c & 0x7f);
X		} while (c & 0x80);
X	}
X	return (value);
X}
X
Xstatic long
Xto32bit(c1,c2,c3,c4)
X{
X	long value = 0L;
X
X	value = (c1 & 0xff);
X	value = (value<<8) + (c2 & 0xff);
X	value = (value<<8) + (c3 & 0xff);
X	value = (value<<8) + (c4 & 0xff);
X	return (value);
X}
X
Xstatic
Xto16bit(c1,c2)
Xint c1, c2;
X{
X	return ((c1 & 0xff ) << 8) + (c2 & 0xff);
X}
X
Xstatic long
Xread32bit()
X{
X	int c1, c2, c3, c4;
X
X	c1 = egetc();
X	c2 = egetc();
X	c3 = egetc();
X	c4 = egetc();
X	return to32bit(c1,c2,c3,c4);
X}
X
Xstatic
Xread16bit()
X{
X	int c1, c2;
X	c1 = egetc();
X	c2 = egetc();
X	return to16bit(c1,c2);
X}
X
X/* static */
Xmferror(s)
Xchar *s;
X{
X	if ( Mf_error )
X		(*Mf_error)(s);
X	exit(1);
X}
X
X/* The code below allows collection of a system exclusive message of */
X/* arbitrary length.  The Msgbuff is expanded as necessary.  The only */
X/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
X
X#define MSGINCREMENT 128
Xstatic char *Msgbuff = NULL;	/* message buffer */
Xstatic int Msgsize = 0;		/* Size of currently allocated Msg */
Xstatic int Msgindex = 0;	/* index of next available location in Msg */
X
Xstatic
Xmsginit()
X{
X	Msgindex = 0;
X}
X
Xstatic char *
Xmsg()
X{
X	return(Msgbuff);
X}
X
Xstatic
Xmsgleng()
X{
X	return(Msgindex);
X}
X
Xstatic
Xmsgadd(c)
Xint c;
X{
X	/* If necessary, allocate larger message buffer. */
X	if ( Msgindex >= Msgsize )
X		biggermsg();
X	Msgbuff[Msgindex++] = c;
X}
X
Xstatic
Xbiggermsg()
X{
X/* 	char *malloc(); */
X	char *newmess;
X	char *oldmess = Msgbuff;
X	int oldleng = Msgsize;
X
X	Msgsize += MSGINCREMENT;
X	newmess = (char *) malloc( (unsigned)(sizeof(char)*Msgsize) );
X
X	if(newmess == NULL)
X		mferror("malloc error!");
X		
X	/* copy old message into larger new one */
X	if ( oldmess != NULL ) {
X		register char *p = newmess;
X		register char *q = oldmess;
X		register char *endq = &oldmess[oldleng];
X
X		for ( ; q!=endq ; p++,q++ )
X			*p = *q;
X		free(oldmess);
X	}
X	Msgbuff = newmess;
X}
X
X/*
X * mfwrite() - The only fuction you'll need to call to write out
X *             a midi file.
X *
X * format      0 - Single multi-channel track
X *             1 - Multiple simultaneous tracks
X *             2 - One or more sequentially independent
X *                 single track patterns                
X * ntracks     The number of tracks in the file.
X * division    This is kind of tricky, it can represent two
X *             things, depending on whether it is positive or negative
X *             (bit 15 set or not).  If  bit  15  of division  is zero,
X *             bits 14 through 0 represent the number of delta-time
X *             "ticks" which make up a quarter note.  If bit  15 of
X *             division  is  a one, delta-times in a file correspond to
X *             subdivisions of a second similiar to  SMPTE  and  MIDI
X *             time code.  In  this format bits 14 through 8 contain
X *             one of four values - 24, -25, -29, or -30,
X *             corresponding  to  the  four standard  SMPTE and MIDI
X *             time code frame per second formats, where  -29
X *             represents  30  drop  frame.   The  second  byte
X *             consisting  of  bits 7 through 0 corresponds the the
X *             resolution within a frame.  Refer the Standard MIDI
X *             Files 1.0 spec for more details.
X * fp          This should be the open file pointer to the file you
X *             want to write.  It will have be a global in order
X *             to work with Mf_putc.  
X */ 
Xvoid 
Xmfwrite(format,ntracks,division,fp) 
Xint format,ntracks,division; 
XFILE *fp; 
X{
X    int i; void mf_write_track_chunk(), mf_write_header_chunk();
X
X    if ( Mf_putc == NULLFUNC )
X	    mferror("mfmf_write() called without setting Mf_putc");
X
X    if ( Mf_writetrack == NULLFUNC )
X	    mferror("mfmf_write() called without setting Mf_mf_writetrack"); 
X
X    /* every MIDI file starts with a header */
X    mf_write_header_chunk(format,ntracks,division);
X
X    /* In format 1 files, the first track is a tempo map */
X    if(format == 1 && ( Mf_writetempotrack ))
X    {
X	(*Mf_writetempotrack)();
X    }
X
X    /* The rest of the file is a series of tracks */
X    for(i = 0; i < ntracks; i++)
X        mf_write_track_chunk(i,fp);
X}
X
Xvoid 
Xmf_write_track_chunk(which_track,fp)
Xint which_track;
XFILE *fp;
X{
X	unsigned long trkhdr,trklength;
X	long offset, place_marker;
X	void write16bit(),write32bit();
X	
X	
X	trkhdr = MTrk;
X	trklength = 0;
X
X	/* Remember where the length was written, because we don't
X	   know how long it will be until we've finished writing */
X	offset = ftell(fp); 
X
X#ifdef DEBUG
X        printf("offset = %d\n",(int) offset);
X#endif
X
X	/* Write the track chunk header */
X	write32bit(trkhdr);
X	write32bit(trklength);
X
X	Mf_numbyteswritten = 0L; /* the header's length doesn't count */
X
X	if( Mf_writetrack )
X	{
X	    (*Mf_writetrack)(which_track);
X	}
X
X	/* mf_write End of track meta event */
X	eputc(0);
X	eputc(meta_event);
X	eputc(end_of_track);
X
X 	eputc(0);
X	 
X	/* It's impossible to know how long the track chunk will be beforehand,
X           so the position of the track length data is kept so that it can
X           be written after the chunk has been generated */
X	place_marker = ftell(fp);
X	
X	/* This method turned out not to be portable because the
X           parameter returned from ftell is not guaranteed to be
X           in bytes on every machine */
X 	/* track.length = place_marker - offset - (long) sizeof(track); */
X
X#ifdef DEBUG
Xprintf("length = %d\n",(int) trklength);
X#endif
X
X 	if(fseek(fp,offset,0) < 0)
X	    mferror("error seeking during final stage of write");
X
X	trklength = Mf_numbyteswritten;
X
X	/* Re-mf_write the track chunk header with right length */
X	write32bit(trkhdr);
X	write32bit(trklength);
X
X	fseek(fp,place_marker,0);
X} /* End gen_track_chunk() */
X
X
Xvoid 
Xmf_write_header_chunk(format,ntracks,division)
Xint format,ntracks,division;
X{
X    unsigned long ident,length;
X    void write16bit(),write32bit();
X    
X    ident = MThd;           /* Head chunk identifier                    */
X    length = 6;             /* Chunk length                             */
X
X    /* individual bytes of the header must be written separately
X       to preserve byte order across cpu types :-( */
X    write32bit(ident);
X    write32bit(length);
X    write16bit(format);
X    write16bit(ntracks);
X    write16bit(division);
X} /* end gen_header_chunk() */
X
X
X/*
X * mf_write_midi_event()
X * 
X * Library routine to mf_write a single MIDI track event in the standard MIDI
X * file format. The format is:
X *
X *                    <delta-time><event>
X *
X * In this case, event can be any multi-byte midi message, such as
X * "note on", "note off", etc.      
X *
X * delta_time - the time in ticks since the last event.
X * type - the type of meta event.
X * chan - The midi channel.
X * data - A pointer to a block of chars containing the META EVENT,
X *        data.
X * size - The length of the meta-event data.
X */
Xint 
Xmf_write_midi_event(delta_time, type, chan, data, size)
Xunsigned long delta_time;
Xunsigned int chan,type;
Xunsigned long size;
Xunsigned char *data;
X{
X    int i;
X    void WriteVarLen();
X    unsigned char c;
X
X    WriteVarLen(delta_time);
X
X    /* all MIDI events start with the type in the first four bits,
X       and the channel in the lower four bits */
X    c = type | chan;
X
X    if(chan > 15)
X        perror("error: MIDI channel greater than 16\n");
X
X    eputc(c);
X
X    /* write out the data bytes */
X    for(i = 0; i < size; i++)
X	eputc(data[i]);
X
X    return(size);
X} /* end mf_write MIDI event */
X
X/*
X * mf_write_meta_event()
X *
X * Library routine to mf_write a single meta event in the standard MIDI
X * file format. The format of a meta event is:
X *
X *          <delta-time><FF><type><length><bytes>
X *
X * delta_time - the time in ticks since the last event.
X * type - the type of meta event.
X * data - A pointer to a block of chars containing the META EVENT,
X *        data.
X * size - The length of the meta-event data.
X */
Xint
Xmf_write_meta_event(delta_time, type, data, size)
Xunsigned long delta_time;
Xunsigned char *data,type;
Xunsigned long size;
X{
X    int i;
X
X    WriteVarLen(delta_time);
X    
X    /* This marks the fact we're writing a meta-event */
X    eputc(meta_event);
X
X    /* The type of meta event */
X    eputc(type);
X
X    /* The length of the data bytes to follow */
X    WriteVarLen(size); 
X
X    for(i = 0; i < size; i++)
X    {
X	if(eputc(data[i]) != data[i])
X	    return(-1); 
X    }
X    return(size);
X} /* end mf_write_meta_event */
X
Xvoid 
Xmf_write_tempo(tempo)
Xunsigned long tempo;
X{
X    /* Write tempo */
X    /* all tempos are written as 120 beats/minute, */
X    /* expressed in microseconds/quarter note     */
X    eputc(0);
X    eputc(meta_event);
X    eputc(set_tempo);
X
X    eputc(3);
X    eputc((unsigned)(0xff & (tempo >> 16)));
X    eputc((unsigned)(0xff & (tempo >> 8)));
X    eputc((unsigned)(0xff & tempo));
X}
X
Xunsigned long 
Xmf_sec2ticks(secs,division,tempo)
Xint division;
Xunsigned int tempo;
Xfloat secs;
X{    
X     return (long)(((secs * 1000.0) / 4.0 * division) / tempo);
X}
X
X/*
X * Write multi-length bytes to MIDI format files
X */
Xvoid 
XWriteVarLen(value)
Xunsigned long value;
X{
X  unsigned long buffer;
X
X  buffer = value & 0x7f;
X  while((value >>= 7) > 0)
X  {
X	buffer <<= 8;
X	buffer |= 0x80;
X	buffer += (value & 0x7f);
X  }
X  while(1){
X       eputc((unsigned)(buffer & 0xff));
X       
X	if(buffer & 0x80)
X		buffer >>= 8;
X	else
X		return;
X	}
X}/* end of WriteVarLen */
X
X/* 
X * This routine converts delta times in ticks into seconds. The
X * else statement is needed because the formula is different for tracks
X * based on notes and tracks based on SMPTE times.
X *
X */
Xfloat 
Xmf_ticks2sec(ticks,division,tempo)
Xint division;
Xunsigned int tempo;
Xunsigned long ticks;
X{
X    float smpte_format, smpte_resolution;
X
X    if(division > 0)
X        return ((float) (((float)(ticks) * (float)(tempo)) / ((float)(division) * 1000000.0)));
X    else
X    {
X       smpte_format = upperbyte(division);
X       smpte_resolution = lowerbyte(division);
X       return (float) ((float) ticks / (smpte_format * smpte_resolution * 1000000.0));
X    }
X} /* end of ticks2sec() */
X
X
X/*
X * write32bit()
X * write16bit()
X *
X * These routines are used to make sure that the byte order of
X * the various data types remains constant between machines. This
X * helps make sure that the code will be portable from one system
X * to the next.  It is slightly dangerous that it assumes that longs
X * have at least 32 bits and ints have at least 16 bits, but this
X * has been true at least on PCs, UNIX machines, and Macintosh's.
X *
X */
Xvoid 
Xwrite32bit(data)
Xunsigned long data;
X{
X    eputc((unsigned)((data >> 24) & 0xff));
X    eputc((unsigned)((data >> 16) & 0xff));
X    eputc((unsigned)((data >> 8 ) & 0xff));
X    eputc((unsigned)(data & 0xff));
X}
X
Xvoid 
Xwrite16bit(data)
Xint data;
X{
X    eputc((unsigned)((data & 0xff00) >> 8));
X    eputc((unsigned)(data & 0xff));
X}
X
X/* write a single character and abort on error */
Xeputc(c)			
Xunsigned char c;
X{
X	int return_val;
X	
X	if((Mf_putc) == NULLFUNC)
X	{
X		mferror("Mf_putc undefined");
X		return(-1);
X	}
X	
X	return_val = (Mf_putc)(c);
X
X	if ( return_val == EOF )
X		mferror("error writing");
X		
X	Mf_numbyteswritten++;
X	return(return_val);
X}
END_OF_FILE
  if test 19054 -ne `wc -c <'midifile/midifile.c'`; then
    echo shar: \"'midifile/midifile.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/midifile.c'
fi
if test -f 'midifile/midifile.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/midifile.doc'\"
else
  echo shar: Extracting \"'midifile/midifile.doc'\" \(13680 characters\)
  sed "s/^X//" >'midifile/midifile.doc' <<'END_OF_FILE'
X
X
X
XMIDIFILE(3)            C LIBRARY FUNCTIONS            MIDIFILE(3)
X
X
X
XNAME
X     mfread,mfwrite - read and write a standard MIDI file
X
XSYNOPSIS
X     #include "mfread.h"
X
X     mfread ()
X
X     int (*Mf_getc) ();
X     int (*Mf_putc) ();
X     int (*Mf_error) (char *msg);
X     int (*Mf_header) (int format, int ntrks, int division);
X     int (*Mf_trackstart) ();
X     int (*Mf_trackend) ();
X     int (*Mf_noteon) (int chan, int pitch, int vol);
X     int (*Mf_noteoff) (int chan, int pitch, int vol);
X     int (*Mf_pressure) (int chan, int pitch, int pressure);
X     int (*Mf_parameter) (int chan, int control, int value);
X     int (*Mf_pitchbend) (int chan, int msb, int lsb);
X     int (*Mf_program) (int chan, int program);
X     int (*Mf_chanpressure) (int chan, int pressure);
X     int (*Mf_sysex) (int leng, char *msg);
X     int (*Mf_metamisc) (int type, int leng, int msg);
X     int (*Mf_seqspecific) (int type, int leng, int msg);
X     int (*Mf_seqnum) (int num);
X     int (*Mf_text) (int type, int leng, int msg);
X     int (*Mf_eot) ();
X     int (*Mf_timesig) (int numer, int denom, int clocks, int qnotes);
X     int (*Mf_smpte) (int hour, int min, int sec, int frame, int fract);
X     int (*Mf_tempo) (int microsecs);
X     int (*Mf_keysig) (int sharpflat, int minor);
X     int (*Mf_arbitrary) (int leng, int msg);
X     int Mf_nomerge;
X     long Mf_currtime;
X
X     mfwrite(int format, int ntracks, int division, FILE *fp)
X
X     int (*Mf_writetrack)(int track);
X     int (*Mf_writetempotrack)();
X
X     void mf_write_midi_event(delta, type, chan, data, size)
X     unsigned long delta;
X     unsigned int type,chan,size;
X     char *data;
X
X     void mf_write_meta_event(delta, type, data, size)
X     unsigned long delta;
X     unsigned int type,chan,size;
X     char *data;
X
X     void mf_write_tempo(tempo)
X     unsigned long tempo;
X
X
X
XSun Release 4.1           Last change:                          1
X
X
X
X
X
X
XMIDIFILE(3)            C LIBRARY FUNCTIONS            MIDIFILE(3)
X
X
X
X     unsigned long mf_sec2ticks(float seconds, int division, int tempo)
X     float seconds;
X     int division;
X     unsigned int tempo;
X
X     float mf_ticks2sec(ticks, division, tempo)
X     unsigned long ticks;
X     int division;
X     unsigned int tempo;
X
X
XDESCRIPTION
X     The mfread function reads  and  inteprets  a  standard  MIDI
X     file.   To use it you need to understand the general form of
X     a MIDI file and the type of information it contains, but you
X     don't  need  to  know  much, if anything, about the detailed
X     format of the file and the mechanics of reading it  reliably
X     and portably.
X
X     The mfwrite function writes a standard MIDI file making  use
X     of  user-defined  functions  that  access the program's data
X     structure.   To  use  it  you  need  to  define   your   own
X     Mf_writetrack  routine and then make use of the write_* fam-
X     ily of routines to write out the  MIDI  data.   The  mfwrite
X     routine  takes  care of the file format and writing the file
X     and track chunk headers.
X
X
XREADING STANDARD MIDI FILES
X     A single call to mfread will read an entire MIDI file.   The
X     interface  to  mfread  is  a set of external variables named
X     Mf_*, most of which are function pointers to be called  from
X     within  mfread  during the process of parsing the MIDI file.
X     Before calling mfread, the  only  requirement  is  that  you
X     assign  a  value  to  Mf_getc - a pointer to a function that
X     will return characters from the MIDI file, using -1 to indi-
X     cate  EOF.   All  the rest of the function pointers are ini-
X     tialized to NULL, and the default action for each is  to  do
X     nothing.   The  following is a complete program using mfread
X     that could serve as a 'syntax checker' for MIDI files:
X
X               #include <stdio.h>
X               #include "midifile.h"
X
X               mygetc()
X               {
X                    /* use standard input */
X                    return(getchar());
X               }
X
X               main()
X               {
X
X
X
XSun Release 4.1           Last change:                          2
X
X
X
X
X
X
XMIDIFILE(3)            C LIBRARY FUNCTIONS            MIDIFILE(3)
X
X
X
X                    Mf_getc = mygetc;
X                    mfread();
X                    exit(0);
X               }
X
X     This takes advantage of the default action when an error  is
X     detected, which is to exit silently with a return code of 1.
X     An error function of your own can be used by giving a  value
X     to Mf_error; the function will be called with the error mes-
X     sage as an argument.  The other Mf_* variables can similarly
X     be  used  to call arbitrary functions while parsing the MIDI
X     file.  The descriptions below of the information  passed  to
X     these  functions  is sparse; refer to the MIDI file standard
X     for the complete descriptions.
X
X     Mf_header is the first function to be called, and its  argu-
X     ments  contain  information from the MIDI file's header; the
X     format (0,1, or 2), the number of tracks, and  the  division
X     of   a   quarter-note   that   defines   the   times  units.
X     Mf_trackstart and Mf_trackend are called  at  the  beginning
X     and end of each track.
X
X     Once inside a track, each separate message causes a function
X     to  be  called.   For  example,  each note-on message causes
X     Mf_noteon to be called with the channel, pitch,  and  volume
X     as  arguments.   The  time  at which the message occurred is
X     stored in Mf_currtime - one of the  few  external  variables
X     that  isn't  a function pointer.  The other channel messages
X     are handled in a similar and obvious fashion  -  Mf_noteoff,
X     Mf_pressure,  Mf_parameter,  Mf_pitchbend,  Mf_program,  and
X     Mf_chanpressure.  See the declarations above for  the  argu-
X     ments that are passed to each.
X
X     System exclusive messages are handled by  calling  Mf_sysex,
X     passing  as  arguments the message length and a pointer to a
X     static buffer containing the entire message.  The buffer  is
X     expanded  when  necessary;  memory  availability is the only
X     limit to its size.  Normally, 'continued' system  exclusives
X     are  automatically merged, and Mf_sysex is only called once.
X     It you want to disable this you can  set  Mf_nomerge  to  1,
X     causing Mf_sysex to be called once for each part of the mes-
X     sage.
X
X     Mf_seqnum is called by the  meta  message  that  provides  a
X     sequence  number, which if present must appear at the begin-
X     ning of a track.  The tempo meta message causes Mf_tempo  to
X     be  called;  its  argument is the number of microseconds per
X     MIDI quarter-note (24 MIDI clocks).  The  end-of-track  meta
X     message  causes Mf_eot to be called.  The key signature meta
X     message causes Mf_keysig to be called;  the  first  argument
X     conveys  the  number of sharps or flats, the second argument
X     is 1 if the key is minor.
X
X
X
XSun Release 4.1           Last change:                          3
X
X
X
X
X
X
XMIDIFILE(3)            C LIBRARY FUNCTIONS            MIDIFILE(3)
X
X
X
X     The Mf_timesig and Mf_smpte functions are  called  when  the
X     corresponding  meta  messages  are  seen.  See the MIDI file
X     standard for a description of their arguments.
X
X     The text messages in the MIDI file standard are of the  fol-
X     lowing types:
X
X               0x01      Text Event
X               0x02      Copyright
X               0x03      Sequence/Track Name
X               0x04      Instrument
X               0x05      Lyric
X               0x06      Marker
X               0x07      Cue Point
X               0x08-0x0F Reserverd but Undefined
X
X     Mf_text is called for each of these; the arguments  are  the
X     type  number,  the message length, and a pointer to the mes-
X     sage buffer.
X
X     Misceallaneous meta messages  are  handled  by  Mf_metamisc,
X     sequencer-specific  messages  are handled by Mf_seqspecific,
X     and arbitrary "escape" messages (started with 0xF7) are han-
X     dled by Mf_arbitrary.
X
XREADING EXAMPLE
X     The following is a strings-like program for MIDI files:
X
X               #include <stdio.h>
X               #include <ctype.h>
X               #include "midifile.h"
X
X               FILE *F;
X
X               mygetc() { return(getc(F)); }
X
X               mytext(type,leng,msg)
X               char *msg;
X               {
X                    char *p;
X                    char *ep = msg + leng;
X
X                    for ( p=msg; p<ep ; p++ )
X                         putchar( isprint(*p) ? *p : '?' );
X                    putchar('0);
X               }
X
X               main(argc,argv)
X               char **argv;
X               {
X                    if ( argc > 1 )
X                         F = fopen(argv[1],"r");
X
X
X
XSun Release 4.1           Last change:                          4
X
X
X
X
X
X
XMIDIFILE(3)            C LIBRARY FUNCTIONS            MIDIFILE(3)
X
X
X
X                    else
X                         F = stdin;
X
X                    Mf_getc = mygetc;
X                    Mf_text = mytext;
X
X                    mfread();
X
X                    exit(0);
X               }
X
X
XWRITING STANDARD MIDI FILES
X     A single call to mfwrite will write  an  entire  MIDI  file.
X     Before  calling  mfwrite, you must assign values to function
X     pointers Mf_writetrack and Mf_putc.  The first is a  routine
X     to  access  your  MIDI data structure, which can make use of
X     other library routines to write the actual MIDI  data.   The
X     routine  Mf_writetrack  will  be  passed  a single parameter
X     which is the number of the track to be written.  The pointer
X     Mf_putc  should  be set to point to a routine that accepts a
X     charcter as input, writes that  character  to  a  file,  and
X     returns the value that was written.  In the case of a format
X     1 file, a routine has to be written to write  a  tempo  map,
X     and  assigned  to  the  function pointer Mf_writetempotrack.
X     This is because format 1 files assume the first track  writ-
X     ten is a tempo track.
X
X     mf_write_midi_event  and  mf_write_meta_event  are  routines
X     that  should  be  called  from your Mf_writetrack routine to
X     write out MIDI events.  The delta time param is  the  number
X     of  ticks  since the last event.  The int "type" is the type
X     of MIDI message. The int "chan" is the MIDI  channel,  which
X     can  be between 1 and 16.  The char pointer "data" points to
X     an array containing the data bytes, if any  exist.  The  int
X     "size" is the number of data bytes.
X
X     mf_sec2ticks and mf_ticks2sec are utility routines  to  help
X     you convert between the MIDI file parameter of ticks and the
X     more standard seconds. The int "division" is the same  divi-
X     sion  parameter from the file header, and tempo is expressed
X     in microseconds  per  MIDI  quarter-note,  or  "24ths  of  a
X     microsecond  per MIDI clock". The division has two meanings,
X     depending on whether bit 15 is set or not.   If  bit  15  of
X     division  is zero, bits 14 through 0 represent the number of
X     delta-time "ticks" which make up a quarter note.  If bit  15
X     of  division  is  a one, delta-times in a file correspond to
X     subdivisions of a second similiar to  SMPTE  and  MIDI  time
X     code.  In  this format bits 14 through 8 contain one of four
X     values - 24, -25, -29, or -30,  corresponding  to  the  four
X     standard  SMPTE and MIDI time code frame per second formats,
X     where  -29  represents  30  drop  frame.   The  second  byte
X
X
X
XSun Release 4.1           Last change:                          5
X
X
X
X
X
X
XMIDIFILE(3)            C LIBRARY FUNCTIONS            MIDIFILE(3)
X
X
X
X     consisting  of  bits 7 through 0 corresponds the the resolu-
X     tion within a frame.  Refer the Standard MIDI Files 1.0 spec
X     for more details.
X
X
XWRITING EXAMPLE
X     The following is a simple  program  to  demonstrate  writing
X     MIDI  files.  The track would consist of a series of quarter
X     notes from lowest to highest in pitch at constant  velocity,
X     each separted by a quarter-note rest.
X
X               #include <stdio.h>
X               #include <ctype.h>
X               #include "midifile.h"
X
X               FILE *fp;
X               myputc(c) { return(putc(c,fp));}
X
X               int mywritetrack(track)
X               int track;
X               {
X                   int i;
X                   char data[2];
X
X                   /* 120 beats/per/second */
X                   mf_write_tempo((long)500000);
X
X                   for(i = 1 ; i < 128; i++){
X                      data[0] = i; /* note number */
X                      data[1] = 64; /* velocity */
X                      if(!mf_write_midi_event(480,note_on,1,data,2))
X                       return(-1);
X                      if(!mf_write_midi_event(480,note_off,1,data,2))
X                          return(-1);
X                   }
X
X                   return(1);
X               } /* end of write_track() */
X
X               main(argc,argv)
X               char **argv;
X               {
X                   if((fp = fopen(argv[1],"w")) == 0L)
X                    exit(1);
X
X                   Mf_putc = myputc;
X                   Mf_writetrack = mywritetrack;
X
X                   /* write a single track */
X                   mfwrite(0,1,480,fp);
X               }
X
X
X
X
XSun Release 4.1           Last change:                          6
X
X
X
X
X
X
XMIDIFILE(3)            C LIBRARY FUNCTIONS            MIDIFILE(3)
X
X
X
XAUTHOR
X     Tim Thompson (att!twitch!glimmer!tjt)
X
XCONTRIBUTORS
X     Michael Czeiszperger (mike@pan.com)
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XSun Release 4.1           Last change:                          7
X
X
X
END_OF_FILE
  if test 13680 -ne `wc -c <'midifile/midifile.doc'`; then
    echo shar: \"'midifile/midifile.doc'\" unpacked with wrong size!
  fi
  # end of 'midifile/midifile.doc'
fi
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  midifile/Makefile midifile/README midifile/crack.c
#   midifile/example1.mid.uu midifile/example2.mid.uu
#   midifile/example3.mid.uu midifile/example4.mid.uu
#   midifile/mfcheck.c midifile/mfstrings.c midifile/mftext.c
#   midifile/mfwrite_ex.c midifile/midifile.3 midifile/midifile.h
#   midifile/noteon.c
# Wrapped by kent@sparky on Wed Aug  7 09:29:28 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'midifile/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/Makefile'\"
else
  echo shar: Extracting \"'midifile/Makefile'\" \(1076 characters\)
  sed "s/^X//" >'midifile/Makefile' <<'END_OF_FILE'
XCFLAGS = -g
X
Xmftext : midifile.o mftext.o crack.o
X	cc midifile.o mftext.o crack.o -o mftext
X
Xmfcheck : midifile.o mfcheck.o
X	cc midifile.o mfcheck.o -o mfcheck
X
Xmfstrings : midifile.o mfstrings.o
X	cc midifile.o mfstrings.o -o mfstrings
X
Xmf1to0 : midifile.o mf1to0.o
X	${CC} ${CFLAGS} $@.c midifile.o -o $@
X
Xmfwrite_ex : midifile.o mfwrite_ex.o
X	cc midifile.o mfwrite_ex.o -o mfwrite_ex
X
Xall : mftext mfcheck mfstrings mf1to0 midifile.man
X
Xmidifile.man : midifile.3
X	nroff -man -Tlp midifile.3 | col -b > midifile.man
X
Xclean :
X	rm -f mftext mfcheck mfstrings mf1to0 mfwrite_ex *.o midifile.man
X
Xlint :
X	lint midifile.c mf1to0.c
X
Xmidtouu :
X	for i in example*.mid ; \
X	do \
X		echo $$i ; \
X		uuencode $$i $$i > `basename $$i .mid`.uu ; \
X		if [ $$? -eq 0 ] ; then  rm -f $$i ; fi ; \
X	done
X
Xuutomid :
X	for i in example*.uu ; \
X	do \
X		echo $$i ; \
X		uudecode $$i ; \
X		if [ $$? -eq 0 ] ; then  rm -f $$i ; fi ; \
X	done
X
Xtest : mftext
X	for i in example*.mid ; \
X	do \
X		echo $$i ; \
X		mftext $$i > /dev/null ; \
X		if [ $$? -ne 0 ] ; then echo "Mftext of $$i failed!" ; fi ; \
X	done
END_OF_FILE
  if test 1076 -ne `wc -c <'midifile/Makefile'`; then
    echo shar: \"'midifile/Makefile'\" unpacked with wrong size!
  fi
  # end of 'midifile/Makefile'
fi
if test -f 'midifile/README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/README'\"
else
  echo shar: Extracting \"'midifile/README'\" \(1559 characters\)
  sed "s/^X//" >'midifile/README' <<'END_OF_FILE'
XThis directory contains a library of code to read and write standard 
XMIDI files, a standard as defined by the MMA, the MIDI Manufacturer's
XAssocation. The format spec is available from the IMA:
X
X                International MIDI Association
X                5316 West 57th Street
X                Los Angeles, CA 90056
X
X
X"make mftext" will compile a program that gives a verbose textual
Xlisting of a MIDI file, and is an example of how to read a file.  "make
Xmf1to0" will compile a program that demonstrates how to write midi
Xfiles, and converts format 1 multitrack files to format 0.  "make
Xmidifile.man" will produce a formatted manual page.  Example MIDI files
Xare named "example*.uu".  They're uuencoded; "make uutomid" will
Xuudecode them all.  The first two examples (example1 and example2) are
Xthe examples given in the 0.06 version of the standard MIDI file
Xspecification.
X
X            ...Tim Thompson...att!twitch!glimmer!tjt...
X
XJune, 1989 - Added code to write midi files and the mf1to0 program.
X	     Michael Czeiszperger  czei@pan.com
X
XNotes on Porting:
XThis library was written on UNIX, and tested on Sun's, PC's and Mac's.  
XIt is distributed in a raw vanilla state, which means you may have to do a 
Xlittle editing depending on the requirements of your particular development 
Xsystem.  For instance, with THINK C on the Mac, you have to be careful 
Xand include all the include files needed by their implementation of 
XUNIX calls such as malloc().  I've left in the proper calls ifdef'd on
XTHINK so you'll have a clue what to do. 
X
Xczei@pan.com
END_OF_FILE
  if test 1559 -ne `wc -c <'midifile/README'`; then
    echo shar: \"'midifile/README'\" unpacked with wrong size!
  fi
  # end of 'midifile/README'
fi
if test -f 'midifile/crack.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/crack.c'\"
else
  echo shar: Extracting \"'midifile/crack.c'\" \(4123 characters\)
  sed "s/^X//" >'midifile/crack.c' <<'END_OF_FILE'
X/*
X * crack - put the command line to the vice.
X *
X * This routine takes four arguments, the variables argc and argv from
X * the program's main procedure, a list of flags to recognize, and an
X * indication whether to ignore unknown flags or not.
X *
X * The action of crack is to read a command line laid out in the format:
X *
X * % command [flags]... [files]...
X *
X * where the flags are of the form -foption, that is, a minus, a character
X * and an optional argument, butted up to the character.  No space may
X * appear between any flag and its option.  The first command line
X * argument not preceeded by '-' is taken as the end of the flags and the
X * beginning of file names.
X * 
X * The flags argument to crack looks like this: "a|b|cd" for flags a b c
X * and d.  In this example, a and b take (optional!) options, as specified
X * by the trailing colon, c and d do not.  When crack scans a flag, it
X * returns the flag character after setting the external character pointer
X * arg_option to the option part.  It also sets arg_index to the index of
X * the argv variable scanned.  Crack returns NULL when it has scanned the
X * last flag.  The value of arg_index then points at the first 
X * argument after the last flag, which should be the first filename, if
X * any.  If there are no arguments, or no more arguments after reading 
X * the flags, arg_index == argc;
X *
X * Flags may be concatenated, for instance, using the flags argument
X * given above: -cd will treat c and d as
X * flags.  -cdaoption also works.  However, tacking what you think is
X * a flag after another flag that takes an option will cause the flag to
X * be lost.  I.e., -ac will treat c as an option, not a flag.
X * 
X * When the ignore flag is zero,  flags scanned that are not in the flags
X * variable generate an error message and crack returns EOF.  If ignore is
X * nonzero, unknown flags are suppressed, that is, no flag is returned.
X * The purpose of ignoring flags is so that more than one part of a
X * program can scan the command line without having to know about the
X * flags of all the other parts.  For instance, where a program calculates
X * a sampling rate by one flag and a value in seconds in another, it must
X * search for the sampling rate first, then the time value.  Two calls to
X * crack() would be required, one to scan just for the flag setting sampling
X * rate, another to ignore the rate flag, but to set the time value based
X * on the sampling rate.
X * NOTE: WHEN MAKING MORE THAN ONE CALL TO crack() IN A PROGRAM, IT
X * IS NECESSARY TO RESET arg_index TO 0 FIRST.
X *
X * When ignoring unknown flags, if an unknown flag has an option
X * associated with it, the option is also ignored.  Care should be excercised
X * here because it may be possible that the associated "option" is really
X * more concatenated flags.  These, if any, are lost.  The rule is that,
X * when ignoring unknown flags, the first instance of an unknown flag
X * causes that flag and the rest of that argument to be discarded.  For
X * instance, if flags is set to "a:b:cd", and a command line:
X * "-zcdaoption" is supplied, c d and a would be ignored because they come
X * after z in the same argument.  The point is there is no way to disambiguate
X * flags from unknown options when ignoring flags, so concatenating options,
X * while nice, is problematical.
X */
X
X#include <stdio.h>
X
Xint arg_index = 0;
Xchar *arg_option;
Xchar *pvcon = NULL;
X
Xchar crack(argc, argv, flags, ign)
X    int argc; char **argv; char *flags; int ign;
X{
X    char *pv, *flgp, *strchr();
X    while ((arg_index) < argc)
X	{
X	if (pvcon != NULL)
X	    pv = pvcon;
X	else
X	    {
X	    if (++arg_index >= argc) return(NULL); 
X	    pv = argv[arg_index];
X	    if (*pv != '-') 
X		return(NULL);
X	    }
X	pv++;		/* skip '-' or prev. flag */
X	if (*pv != NULL)
X	    {
X	    if ((flgp=strchr(flags,*pv)) != NULL)
X		{
X		pvcon = pv;
X		if (*(flgp+1) == '|') { arg_option = pv+1; pvcon = NULL; }
X		return(*pv);
X		}
X	    else
X		if (!ign)
X		    {
X		    fprintf(stderr, "%s: no such flag: %s\n", argv[0], pv);
X		    return(EOF);
X		    }
X		else
X		    pvcon = NULL;
X	    }
X	pvcon = NULL;
X	}
X    return(NULL);
X    }
X
END_OF_FILE
  if test 4123 -ne `wc -c <'midifile/crack.c'`; then
    echo shar: \"'midifile/crack.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/crack.c'
fi
if test -f 'midifile/example1.mid.uu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/example1.mid.uu'\"
else
  echo shar: Extracting \"'midifile/example1.mid.uu'\" \(141 characters\)
  sed "s/^X//" >'midifile/example1.mid.uu' <<'END_OF_FILE'
Xbegin 660 example1.mid
XM351H9     8    ! &!-5')K    .P#_6 0$ A@( /]1 P>A( # !0#!+@#"
XD1@"2,&  /&!@D4- 8)!,((% @C!  #Q  (%#0 " 3$  _R\ 
X 
Xend
END_OF_FILE
  if test 141 -ne `wc -c <'midifile/example1.mid.uu'`; then
    echo shar: \"'midifile/example1.mid.uu'\" unpacked with wrong size!
  fi
  # end of 'midifile/example1.mid.uu'
fi
if test -f 'midifile/example2.mid.uu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/example2.mid.uu'\"
else
  echo shar: Extracting \"'midifile/example2.mid.uu'\" \(195 characters\)
  sed "s/^X//" >'midifile/example2.mid.uu' <<'END_OF_FILE'
Xbegin 660 example2.mid
XM351H9     8  0 $ &!-5')K    % #_6 0$ A@( /]1 P>A((, _R\ 351R
XM:P   !  P 6!0)!,((% 3   _R\ 351R:P    \ P2Y@D4- @B!#  #_+P!-
X<5')K    %0#"1@"2,&  /&"# #   #P  /\O  \ 
X 
Xend
END_OF_FILE
  if test 195 -ne `wc -c <'midifile/example2.mid.uu'`; then
    echo shar: \"'midifile/example2.mid.uu'\" unpacked with wrong size!
  fi
  # end of 'midifile/example2.mid.uu'
fi
if test -f 'midifile/example3.mid.uu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/example3.mid.uu'\"
else
  echo shar: Extracting \"'midifile/example3.mid.uu'\" \(191 characters\)
  sed "s/^X//" >'midifile/example3.mid.uu' <<'END_OF_FILE'
Xbegin 660 example3.mid
XM351H9     8    ! !A-5')K    7P#_ 29$('-C86QE(" @(" @(" @(" @
XM(" @(" @(" @(" @(" @(" @( "0/D ,/@ ,0$ ,0  ,0D ,0@ ,0T ,0P ,
X;14 ,10 ,1T ,1P ,24 ,20 ,2D ,2@ ,_R\ 
X 
Xend
END_OF_FILE
  if test 191 -ne `wc -c <'midifile/example3.mid.uu'`; then
    echo shar: \"'midifile/example3.mid.uu'\" unpacked with wrong size!
  fi
  # end of 'midifile/example3.mid.uu'
fi
if test -f 'midifile/example4.mid.uu' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/example4.mid.uu'\"
else
  echo shar: Extracting \"'midifile/example4.mid.uu'\" \(2519 characters\)
  sed "s/^X//" >'midifile/example4.mid.uu' <<'END_OF_FILE'
Xbegin 660 example4.mid
XM351H9     8    ! #!-5')K   &]P#_ 28Y-G!P<3$L,B @(" @(" @(" @
XM(" @(" @(" @(" @(" @(" @( "0*U  /E@ .U  05 $D4]T%) ^   [  !!
XM  613F $3P /D"L  9%/7 !. !I19 !/ !!1  %/:A5/ $^0*T0 .E@ /5  
XM0&@"D4]T%I Z   ]  !   613EP"3P /3V@ 3@ "D"L &)%19 !/ !-/: 51
XM !)/ $U37 &0*T0 /E  .TX 05 0D5,  E10!I ^   [  !!  614UL#5  ,
XM4F0"4P "D"L #9%2 "%15 *0*T0 .E0 /5  0$T.D5)4 E$ ") Z   ]  ! 
XM  214@ "45H/4%0#D"L  9%1  E0 ")/6 20*T@ /EH .U  05H#D4\ %9 ^
XM   [  !!  :10U0*0P #148%D"L  9%#9 %%  A"= 1# !5"  %#7!%$6P!#
XM !E$  )%9!%&4 )% !!&  M'9!!'  *0)$0"D4AH+I \1@ W0 ! 3  D  *1
XM2  6D#P  #<  $  !9%(6P=(  M(9 &0*T8*D4@ )4A< 9 \2  W.P! 4  K
XM  N12  -D#P  #<  $  &"1( Y%(7"V0/$T -T0 0$\ )  !D4@ %Y \   W
XM  !  !@K1@&11UP51P :D#Q0 #=( $!0 "L  I%(7!:0/   -P  0  $D4@ 
XM DE;#4D  DIH Y F2# W2  [3@ U2  ^*P F !@W   [   U   ^ !@K2":1
XM2@ *D#Y. #=. #5& #M( "L &#X  #<  #4  #L &"9 &I%#7 Q%: 1#  :0
XM/DT -T@ -3X .T< )@ !D44  D-D"T)H!$, !I ^   W   U   [  Z10@ "
XM0V8(D"M,#9%-9 !# !E*9 %-  F0/DX -T8 .TT -3T *P (D4=< TH #9 ^
XM   W   [   U  611P #0UP0D"], )%#  )*="Z0/E  -4@ -TP .T\ +P #
XMD4H %9 ^   U   W   [  612F@)2@ (2F0"D"M$#Y%* "!*:@&0/E  -T@ 
XM-44 .T@ *P 1D4H !Y ^   W   U   [ !@F4 &12F@OD#Y, #=( #M- #5 
XM "8  I%)8 )* !20/@  -P  .P  -0 %D4D  4IH$I K2 .12UP"2@ 72P "
XM4V@2D#Y0 #5& #=/ #M0 "L #I%27 )3  B0/@  -0  -P  .P %D5%8 %( 
XM#5!;!%$  I D1PF13V0!4  FD#Q, #=0 $!4 "0 &#P  #<  $  %Y%/  &0
XM*TP?D4AD#4QF 4@  Y \3  W0 ! 4  K  B13  $1V0+2UL!D#P  #<  $  
XM Y%'  M+  -*7 -&9 20)$TP/%@ -UH 0%@ )  5D48  Y \   W  !   B1
XM2@ 0D"9$&"8 &"A/ #Q8 #=0 $!:"I%+= Z0*   /   -P  0   D4L "4QJ
XM#Y I3 "13  "370ND#E0 #Q( $%- "D  Y%- !60.0  /   00 "D4QD#DP 
XM!DUH I D3"2130 ,D#E/ #Q( $%. "0 &)%/7 "0.0  /   00 1D4\ !$U;
XM Y I4# Y3  \3@!!3  I !@Y   \  !!  :130 2D"1',#E( #Q0 $%4 "0 
XM Y%/:!)/  .0.0  /   00 %D4U<$$T  Y P3 &13%POD#=, #Q4 $!4 #  
XM!Y%, !&0-P  /   0  $D4M;#4L !TQH ) K1!"13  +2UP.2P "2F0%D#Q0
XM #=  $!0 "L #I%*  J0/   -P  0  #D4E4#4D !DAD I P4# \4  W0 ! 
XM2  P  :12  2D#P  #<  $  &"](&"\ &)%'6 "0+4@ /%@ -T@ 0$@0D4A4
XM $< ") M   \   W  !   2125P%2  ,2F@#D"M$ )%) !%* !^0/D@ -40 
XM-T< .T  *P !D4-'$454 $, !I ^   U   W   [  *11EL&10 )1VH&1@ !
XMD"90")%' "8^9 *0/DX -40 -T0 .T0 )@ +D3]< CX "Y ^   U   W   [
XM  210'0 /P -0  '070 D"M(&I%!  )%9!)%  )(9 "0/D\ -U  -4T .TX 
XM*P 8/@  -P  -0  .P !D4@  TQJ$$P !) F3 .12G062@ ,3UL+D#L[ #X^
XM #5  #=  "8 "I%37 1/  J0.P  /@  -P 'D5, !E9T!Y U  0D3P \7@61
XM5%P 5@ 3D#P $9%4  >0)  8+4P .5@8*U@ -UP +0 4.0 $*P  -P 8*%X 
XM-%@8*   -  8)%\ ,&@8,  8)  >D5M3!UU8!%L !%]<!%T !&!T U\ "6  
X%@T__+P  
X 
Xend
END_OF_FILE
  if test 2519 -ne `wc -c <'midifile/example4.mid.uu'`; then
    echo shar: \"'midifile/example4.mid.uu'\" unpacked with wrong size!
  fi
  # end of 'midifile/example4.mid.uu'
fi
if test -f 'midifile/mfcheck.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/mfcheck.c'\"
else
  echo shar: Extracting \"'midifile/mfcheck.c'\" \(129 characters\)
  sed "s/^X//" >'midifile/mfcheck.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include "midifile.h"
X
Xmygetc() { return(getchar()); }
X
Xmain()
X{
X	Mf_getc = mygetc;
X	midifile ();
X	exit(0);
X}
END_OF_FILE
  if test 129 -ne `wc -c <'midifile/mfcheck.c'`; then
    echo shar: \"'midifile/mfcheck.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/mfcheck.c'
fi
if test -f 'midifile/mfstrings.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/mfstrings.c'\"
else
  echo shar: Extracting \"'midifile/mfstrings.c'\" \(414 characters\)
  sed "s/^X//" >'midifile/mfstrings.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <ctype.h>
X#include "midifile.h"
X
XFILE *F;
X
Xmygetc() { return(getc(F)); }
X
Xmytext(type,leng,msg)
Xchar *msg;
X{
X	char *p;
X	char *ep = msg + leng;
X
X	for ( p=msg; p<ep ; p++ )
X		putchar( isprint(*p) ? *p : '?' );
X	putchar('\n');
X}
X
Xmain(argc,argv)
Xchar **argv;
X{
X	if ( argc > 1 )
X		F = fopen(argv[1],"r");
X	else
X		F = stdin;
X
X	Mf_getc = mygetc;
X	Mf_text = mytext;
X
X	midifile();
X
X	exit(0);
X}
END_OF_FILE
  if test 414 -ne `wc -c <'midifile/mfstrings.c'`; then
    echo shar: \"'midifile/mfstrings.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/mfstrings.c'
fi
if test -f 'midifile/mftext.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/mftext.c'\"
else
  echo shar: Extracting \"'midifile/mftext.c'\" \(4885 characters\)
  sed "s/^X//" >'midifile/mftext.c' <<'END_OF_FILE'
X/*
X * mftext
X * 
X * Convert a MIDI file to verbose text.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "midifile.h"
X
Xstatic FILE *F;
Xint SECONDS;      /* global that tells whether to display seconds or ticks */
Xint division;        /* from the file header */
Xlong tempo = 500000; /* the default tempo is 120 beats/minute */
X
Xfilegetc()
X{
X	return(getc(F));
X}
X
X/* for crack */
Xextern int arg_index;
X
Xmain(argc,argv)
Xchar **argv;
X{
X	FILE *efopen();
X	char ch;
X
X	SECONDS = 0;
X
X	while((ch = crack(argc,argv,"s",0)) != NULL){
X	    switch(ch){
X		case 's' : SECONDS = 1; break;
X		}
X	    }
X
X	if ( argc < 2 && !SECONDS || argc < 3 && SECONDS )
X	    F = stdin;
X	else
X	    F = efopen(argv[arg_index],"r");
X
X	initfuncs();
X	Mf_getc = filegetc;
X	midifile();
X	fclose(F);
X	exit(0);
X}
X
XFILE *
Xefopen(name,mode)
Xchar *name;
Xchar *mode;
X{
X	FILE *f;
X	extern int errno;
X	extern char *sys_errlist[];
X	extern int sys_nerr;
X	char *errmess;
X
X	if ( (f=fopen(name,mode)) == NULL ) {
X		(void) fprintf(stderr,"*** ERROR *** Cannot open '%s'!\n",name);
X		if ( errno <= sys_nerr )
X			errmess = sys_errlist[errno];
X		else
X			errmess = "Unknown error!";
X		(void) fprintf(stderr,"************* Reason: %s\n",errmess);
X		exit(1);
X	}
X	return(f);
X}
X
Xerror(s)
Xchar *s;
X{
X	fprintf(stderr,"Error: %s\n",s);
X}
X
Xtxt_header(format,ntrks,ldivision)
X{
X        division = ldivision; 
X	printf("Header format=%d ntrks=%d division=%d\n",format,ntrks,division);
X}
X
Xtxt_trackstart()
X{
X	printf("Track start\n");
X}
X
Xtxt_trackend()
X{
X	printf("Track end\n");
X}
X
Xtxt_noteon(chan,pitch,vol)
X{
X	prtime();
X	printf("Note on, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol);
X}
X
Xtxt_noteoff(chan,pitch,vol)
X{
X	prtime();
X	printf("Note off, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol);
X}
X
Xtxt_pressure(chan,pitch,press)
X{
X	prtime();
X	printf("Pressure, chan=%d pitch=%d press=%d\n",chan+1,pitch,press);
X}
X
Xtxt_parameter(chan,control,value)
X{
X	prtime();
X	printf("Parameter, chan=%d c1=%d c2=%d\n",chan+1,control,value);
X}
X
Xtxt_pitchbend(chan,msb,lsb)
X{
X	prtime();
X	printf("Pitchbend, chan=%d msb=%d lsb=%d\n",chan+1,msb,lsb);
X}
X
Xtxt_program(chan,program)
X{
X	prtime();
X	printf("Program, chan=%d program=%d\n",chan+1,program);
X}
X
Xtxt_chanpressure(chan,press)
X{
X	prtime();
X	printf("Channel pressure, chan=%d pressure=%d\n",chan+1,press);
X}
X
Xtxt_sysex(leng,mess)
Xchar *mess;
X{
X	prtime();
X	printf("Sysex, leng=%d\n",leng);
X}
X
Xtxt_metamisc(type,leng,mess)
Xchar *mess;
X{
X	prtime();
X	printf("Meta event, unrecognized, type=0x%02x leng=%d\n",type,leng);
X}
X
Xtxt_metaspecial(type,leng,mess)
Xchar *mess;
X{
X	prtime();
X	printf("Meta event, sequencer-specific, type=0x%02x leng=%d\n",type,leng);
X}
X
Xtxt_metatext(type,leng,mess)
Xchar *mess;
X{
X	static char *ttype[] = {
X		NULL,
X		"Text Event",		/* type=0x01 */
X		"Copyright Notice",	/* type=0x02 */
X		"Sequence/Track Name",
X		"Instrument Name",	/* ...       */
X		"Lyric",
X		"Marker",
X		"Cue Point",		/* type=0x07 */
X		"Unrecognized"
X	};
X	int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1;
X	register int n, c;
X	register char *p = mess;
X
X	if ( type < 1 || type > unrecognized )
X		type = unrecognized;
X	prtime();
X	printf("Meta Text, type=0x%02x (%s)  leng=%d\n",type,ttype[type],leng);
X	printf("     Text = <");
X	for ( n=0; n<leng; n++ ) {
X		c = *p++;
X		printf( (isprint(c)||isspace(c)) ? "%c" : "\\0x%02x" , c);
X	}
X	printf(">\n");
X}
X
Xtxt_metaseq(num)
X{
X	prtime();
X	printf("Meta event, sequence number = %d\n",num);
X}
X
Xtxt_metaeot()
X{
X	prtime();
X	printf("Meta event, end of track\n");
X}
X
Xtxt_keysig(sf,mi)
X{
X	prtime();
X	printf("Key signature, sharp/flats=%d  minor=%d\n",sf,mi);
X}
X
Xtxt_tempo(ltempo)
Xlong ltempo;
X{
X	tempo = ltempo;
X	prtime();
X	printf("Tempo, microseconds-per-MIDI-quarter-note=%d\n",tempo);
X}
X
Xtxt_timesig(nn,dd,cc,bb)
X{
X	int denom = 1;
X	while ( dd-- > 0 )
X		denom *= 2;
X	prtime();
X	printf("Time signature=%d/%d  MIDI-clocks/click=%d  32nd-notes/24-MIDI-clocks=%d\n",
X		nn,denom,cc,bb);
X}
X
Xtxt_smpte(hr,mn,se,fr,ff)
X{
X	prtime();
X	printf("SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d\n",
X		hr,mn,se,fr,ff);
X}
X
Xtxt_arbitrary(leng,mess)
Xchar *mess;
X{
X	prtime();
X	printf("Arbitrary bytes, leng=%d\n",leng);
X}
X
Xprtime()
X{
X    if(SECONDS)
X	printf("Time=%f   ",mf_ticks2sec(Mf_currtime,division,tempo));
X    else
X	printf("Time=%ld  ",Mf_currtime);
X}
X
Xinitfuncs()
X{
X	Mf_error = error;
X	Mf_header =  txt_header;
X	Mf_trackstart =  txt_trackstart;
X	Mf_trackend =  txt_trackend;
X	Mf_noteon =  txt_noteon;
X	Mf_noteoff =  txt_noteoff;
X	Mf_pressure =  txt_pressure;
X	Mf_parameter =  txt_parameter;
X	Mf_pitchbend =  txt_pitchbend;
X	Mf_program =  txt_program;
X	Mf_chanpressure =  txt_chanpressure;
X	Mf_sysex =  txt_sysex;
X	Mf_metamisc =  txt_metamisc;
X	Mf_seqnum =  txt_metaseq;
X	Mf_eot =  txt_metaeot;
X	Mf_timesig =  txt_timesig;
X	Mf_smpte =  txt_smpte;
X	Mf_tempo =  txt_tempo;
X	Mf_keysig =  txt_keysig;
X	Mf_seqspecific =  txt_metaspecial;
X	Mf_text =  txt_metatext;
X	Mf_arbitrary =  txt_arbitrary;
X}
END_OF_FILE
  if test 4885 -ne `wc -c <'midifile/mftext.c'`; then
    echo shar: \"'midifile/mftext.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/mftext.c'
fi
if test -f 'midifile/mfwrite_ex.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/mfwrite_ex.c'\"
else
  echo shar: Extracting \"'midifile/mfwrite_ex.c'\" \(1158 characters\)
  sed "s/^X//" >'midifile/mfwrite_ex.c' <<'END_OF_FILE'
X/*
X * writing_example.c 4/30/89
X *
X */
X#include <stdio.h>
X#include <ctype.h>
X#include "midifile.h"
X
X/* These lines are needed to use the library */
XFILE *fp;
Xmyputc(c) { return(putc(c,fp));}
X
X
X/*
X * mywritetrack()
X *
X * Sample showing how to use the library routines to write out a track.
X * Returns 1 if successful, and -1 if not.  The track consists of
X * a series of quarter notes from lowest to highest in pitch at
X * constant velocity, each separted by a quarter-note rest.
X *
X */
Xint mywritetrack(track)
Xint track;
X{
X    int i;
X    char data[2];
X
X    mf_write_tempo((long)500000); /* 120 beats/per/second */
X
X    for(i = 1 ; i < 128; i++){
X       data[0] = i; /* note number */ 
X       data[1] = 64; /* velocity */ 
X       if(!mf_write_midi_event(480,note_on,1,data,2)) return(-1);
X       if(!mf_write_midi_event(480,note_off,1,data,2)) return(-1);
X    }
X    return(1);
X} /* end of write_track() */
X
Xmain(argc,argv)
Xchar **argv;
X{
X    if((fp = fopen(argv[1],"w")) == 0L)
X	printf("f1to0: unable to open file %s for writing.\n",argv[1]);
X
X    Mf_putc = myputc;
X    Mf_writetrack = mywritetrack;
X
X    /* write a single track */
X    mfwrite(0,1,480,fp);
X}
END_OF_FILE
  if test 1158 -ne `wc -c <'midifile/mfwrite_ex.c'`; then
    echo shar: \"'midifile/mfwrite_ex.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/mfwrite_ex.c'
fi
if test -f 'midifile/midifile.3' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/midifile.3'\"
else
  echo shar: Extracting \"'midifile/midifile.3'\" \(10710 characters\)
  sed "s/^X//" >'midifile/midifile.3' <<'END_OF_FILE'
X.TH MIDIFILE 3
X.SH NAME
Xmfread,mfwrite - read and write a standard MIDI file
X.SH SYNOPSIS
X\fC#include "mfread.h"
X
Xmfread ()
X
X.nf
Xint (*Mf_getc) ();
Xint (*Mf_putc) ();
Xint (*Mf_error) (char *msg);
Xint (*Mf_header) (int format, int ntrks, int division);
Xint (*Mf_trackstart) ();
Xint (*Mf_trackend) ();
Xint (*Mf_noteon) (int chan, int pitch, int vol);
Xint (*Mf_noteoff) (int chan, int pitch, int vol);
Xint (*Mf_pressure) (int chan, int pitch, int pressure);
Xint (*Mf_parameter) (int chan, int control, int value);
Xint (*Mf_pitchbend) (int chan, int msb, int lsb);
Xint (*Mf_program) (int chan, int program);
Xint (*Mf_chanpressure) (int chan, int pressure);
Xint (*Mf_sysex) (int leng, char *msg);
Xint (*Mf_metamisc) (int type, int leng, int msg);
Xint (*Mf_seqspecific) (int type, int leng, int msg);
Xint (*Mf_seqnum) (int num);
Xint (*Mf_text) (int type, int leng, int msg);
Xint (*Mf_eot) ();
Xint (*Mf_timesig) (int numer, int denom, int clocks, int qnotes);
Xint (*Mf_smpte) (int hour, int min, int sec, int frame, int fract);
Xint (*Mf_tempo) (int microsecs);
Xint (*Mf_keysig) (int sharpflat, int minor);
Xint (*Mf_arbitrary) (int leng, int msg);
Xint Mf_nomerge;
Xlong Mf_currtime;
X.fi
X.sp 1
Xmfwrite(int format, int ntracks, int division, FILE *fp)
X.sp 1
X.nf
Xint (*Mf_writetrack)(int track);
Xint (*Mf_writetempotrack)();
X
Xvoid mf_write_midi_event(delta, type, chan, data, size)
Xunsigned long delta;
Xunsigned int type,chan,size;
Xchar *data;
X
Xvoid mf_write_meta_event(delta, type, data, size)
Xunsigned long delta;
Xunsigned int type,chan,size;
Xchar *data;
X
Xvoid mf_write_tempo(tempo)
Xunsigned long tempo;
X
Xunsigned long mf_sec2ticks(float seconds, int division, int tempo)
Xfloat seconds;
Xint division;
Xunsigned int tempo;
X
Xfloat mf_ticks2sec(ticks, division, tempo)
Xunsigned long ticks;
Xint division;
Xunsigned int tempo;
X.fi
X
X.SH DESCRIPTION
XThe \fCmfread\fR function reads and inteprets a standard MIDI file.
XTo use it you need to understand the general form of a
XMIDI file and the type of information it contains, but you don't
Xneed to know much, if anything, about the detailed format of the file
Xand the mechanics of reading it reliably and portably.
X
XThe \fCmfwrite\fR function writes a standard MIDI file making
Xuse of user-defined functions that access the program's
Xdata structure.  To use it you need to define your own Mf_writetrack
Xroutine and then make use of the write_* family of routines to
Xwrite out the MIDI data.  The \fCmfwrite\fR routine takes
Xcare of the file format and writing the file and track chunk headers. 
X
X.SH READING STANDARD MIDI FILES
XA single call to \fCmfread\fR will read an entire MIDI file.
XThe interface to \fCmfread\fR is a set of external variables
Xnamed \fCMf_*\fR, most of which are function pointers to be called
Xfrom within \fCmfread\fR during the process of parsing the MIDI file.
XBefore calling \fCmfread\fR, the only
Xrequirement is that you assign a value
Xto \fCMf_getc\fR - a pointer to a function that will return
Xcharacters from the MIDI file, using -1 to indicate EOF.
XAll the rest of the function
Xpointers are initialized to NULL, and the default action for each
Xis to do nothing.  The following is a complete program using \fCmfread\fR
Xthat could serve as a 'syntax checker' for MIDI files:
X
X.in +1i
X.ft C
X.nf
X#include <stdio.h>
X#include "midifile.h"
X
Xmygetc()
X{
X	/* use standard input */
X	return(getchar());
X}
X
Xmain()
X{
X	Mf_getc = mygetc;
X	mfread();
X	exit(0);
X}
X.fi
X.ft R
X.in -1i
X
XThis takes advantage of the default action when an error is detected, which
Xis to exit silently with a return code of 1.  An error function of your
Xown can be used by giving a value to \fCMf_error\fR; the function will be
Xcalled with the error message as an argument.
XThe other \fCMf_* variables can similarly be used to call arbitrary
Xfunctions while parsing the MIDI file.  The descriptions below
Xof the information passed to these functions is sparse; refer to
Xthe MIDI file standard for the complete descriptions.
X
X\fCMf_header\fR is the first function to be called, and its arguments
Xcontain information from the MIDI file's header; the format (0,1, or 2),
Xthe number of tracks, and the division of a quarter-note that defines
Xthe times units.
X\fCMf_trackstart\fR and
X\fCMf_trackend\fR are called at the beginning and end of each track.
X
XOnce inside a track, each separate message causes a function to be called.
XFor example, each note-on message causes \fCMf_noteon\fR to be called
Xwith the channel, pitch, and volume as arguments.  The time at which
Xthe message occurred is stored in \fCMf_currtime\fR - one of the few
Xexternal variables that isn't a function pointer.  The other channel messages
Xare handled in a similar and obvious fashion -
X\fCMf_noteoff\fR,
X\fCMf_pressure\fR,
X\fCMf_parameter\fR,
X\fCMf_pitchbend\fR,
X\fCMf_program\fR,
Xand \fCMf_chanpressure\fR.  See the declarations above for the arguments
Xthat are passed to each.
X
XSystem exclusive messages are handled by calling \fCMf_sysex\fR, passing
Xas arguments the message length and a pointer to a static buffer containing
Xthe entire message.
XThe buffer is expanded when necessary; memory availability is the only limit
Xto its size.  Normally, 'continued' system exclusives are automatically
Xmerged, and \fCMf_sysex\fR is only called once.  It you want to disable this
Xyou can set \fCMf_nomerge\fR to 1, causing \fCMf_sysex\fR to be called
Xonce for each part of the message.
X
X\fCMf_seqnum\fR is called by the \fImeta\fR message that provides
Xa sequence number,
Xwhich if present must appear at the beginning of a track.
XThe tempo \fImeta\fR message causes \fCMf_tempo\fR to be called; its
Xargument is the number of microseconds per MIDI quarter-note (24 MIDI clocks).
XThe end-of-track \fImeta\fR message causes \fCMf_eot\fR to be called.
XThe key signature \fImeta\fR message causes \fCMf_keysig\fR to be called;
Xthe first argument conveys the number of sharps or flats, the second
Xargument is 1 if the key is minor.
X
XThe \fCMf_timesig\fR and \fCMf_smpte\fR functions are called when the
Xcorresponding \fImeta\fR messages are seen.  See the MIDI file standard
Xfor a description of their arguments.
X
XThe \fItext\fR messages in the MIDI file standard are of the following
Xtypes:
X
X.in +1i
X.nf
X0x01		Text Event
X0x02		Copyright
X0x03		Sequence/Track Name
X0x04		Instrument
X0x05		Lyric
X0x06		Marker
X0x07		Cue Point
X0x08-0x0F	Reserverd but Undefined
X.fi
X.in -1i
X
X\fCMf_text\fR is called for each of these; the arguments are
Xthe type number, the message length, and a pointer to the message buffer.
X
XMisceallaneous \fImeta\fR messages are handled by \fCMf_metamisc\fR,
Xsequencer-specific messages are handled by \fCMf_seqspecific\fR, and
Xarbitrary "escape" messages (started with 0xF7) are handled by
X\fCMf_arbitrary\fR.
X.SH READING EXAMPLE
XThe following is a \fCstrings\fR-like program for MIDI files:
X
X.in +1i
X.ft C
X.nf
X#include <stdio.h>
X#include <ctype.h>
X#include "midifile.h"
X
XFILE *F;
X
Xmygetc() { return(getc(F)); }
X
Xmytext(type,leng,msg)
Xchar *msg;
X{
X	char *p;
X	char *ep = msg + leng;
X
X	for ( p=msg; p<ep ; p++ )
X		putchar( isprint(*p) ? *p : '?' );
X	putchar('\n');
X}
X
Xmain(argc,argv)
Xchar **argv;
X{
X	if ( argc > 1 )
X		F = fopen(argv[1],"r");
X	else
X		F = stdin;
X
X	Mf_getc = mygetc;
X	Mf_text = mytext;
X
X	mfread();
X
X	exit(0);
X}
X.fi
X.ft R
X.in -1i
X.sp
X.SH WRITING STANDARD MIDI FILES
XA single call to \fCmfwrite\fR will write an entire MIDI file.  Before
Xcalling \fCmfwrite\fR, you must assign values to function pointers
X\fCMf_writetrack\fR and \fCMf_putc\fR.  The first is a routine to
Xaccess your MIDI data structure, which can make use of other library
Xroutines to write the actual MIDI data.  The routine
X\fCMf_writetrack\fR will be passed a single parameter which is the
Xnumber of the track to be written.  The pointer \fCMf_putc\fR should be
Xset to point to a routine that accepts a charcter as input, writes that
Xcharacter to a file, and returns the value that was written.  In the
Xcase of a format 1 file, a routine has to be written to write a tempo
Xmap, and assigned to the function pointer \fCMf_writetempotrack\fR.
XThis is because format 1 files assume the first track written is a
Xtempo track.
X
X\fCmf_write_midi_event\fR and \fCmf_write_meta_event\fR are routines
Xthat should be called from your \fCMf_writetrack\fR routine to write
Xout MIDI events.  The delta time param is the number of ticks since the
Xlast event.  The int "type" is the type of MIDI message. The int "chan"
Xis the MIDI channel, which can be between 1 and 16.  The char pointer
X"data" points to an array containing the data bytes, if any exist. The
Xint "size" is the number of data bytes.
X
X\fCmf_sec2ticks\fR and \fCmf_ticks2sec\fR are utility routines
Xto help you convert between the MIDI file parameter of ticks
Xand the more standard seconds. The int "division" is the same
Xdivision parameter from the file header, and tempo is expressed
Xin microseconds per MIDI quarter-note, or "24ths of a microsecond
Xper MIDI clock". The division has two meanings, depending on
Xwhether bit 15 is set or not.  If bit 15 of division is zero,
Xbits 14 through 0 represent the number of delta-time "ticks"
Xwhich make up a quarter note.  If bit 15 of division is a one,
Xdelta-times in a file correspond to subdivisions of a second
Xsimiliar to SMPTE and MIDI time code. In this format bits
X14 through 8 contain one of four values - 24, -25, -29, or -30,
Xcorresponding to the four standard SMPTE and MIDI time code
Xframe per second formats, where -29 represents 30 drop frame.
XThe second byte consisting of bits 7 through 0 corresponds
Xthe the resolution within a frame.  Refer the Standard MIDI Files 
X1.0 spec for more details.
X
X.SH WRITING EXAMPLE
XThe following is a simple program to demonstrate writing MIDI files.
XThe track would consist of a series of quarter notes from lowest to
Xhighest in pitch at constant velocity, each separted by a quarter-note
Xrest.
X.sp
X.in +1i
X.ft C
X.nf
X#include <stdio.h>
X#include <ctype.h>
X#include "midifile.h"
X
XFILE *fp;
Xmyputc(c) { return(putc(c,fp));}
X
Xint mywritetrack(track)
Xint track;
X{
X    int i;
X    char data[2];
X
X    /* 120 beats/per/second */
X    mf_write_tempo((long)500000); 
X
X    for(i = 1 ; i < 128; i++){
X       data[0] = i; /* note number */
X       data[1] = 64; /* velocity */
X       if(!mf_write_midi_event(480,note_on,1,data,2)) 
X	   return(-1);
X       if(!mf_write_midi_event(480,note_off,1,data,2)) 
X           return(-1);
X    }
X
X    return(1);
X} /* end of write_track() */
X
Xmain(argc,argv)
Xchar **argv;
X{
X    if((fp = fopen(argv[1],"w")) == 0L)
X	exit(1);
X
X    Mf_putc = myputc;
X    Mf_writetrack = mywritetrack;
X
X    /* write a single track */
X    mfwrite(0,1,480,fp);
X}
X.sp
X.fi
X.ft R
X.in -1i
X.sp
X.SH AUTHOR
XTim Thompson (att!twitch!glimmer!tjt)
X.SH CONTRIBUTORS
XMichael Czeiszperger (mike@pan.com)
END_OF_FILE
  if test 10710 -ne `wc -c <'midifile/midifile.3'`; then
    echo shar: \"'midifile/midifile.3'\" unpacked with wrong size!
  fi
  # end of 'midifile/midifile.3'
fi
if test -f 'midifile/midifile.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/midifile.h'\"
else
  echo shar: Extracting \"'midifile/midifile.h'\" \(3285 characters\)
  sed "s/^X//" >'midifile/midifile.h' <<'END_OF_FILE'
X/* definitions for MIDI file parsing code */
Xextern int (*Mf_getc)();
Xextern int (*Mf_header)();
Xextern int (*Mf_trackstart)();
Xextern int (*Mf_trackend)();
Xextern int (*Mf_noteon)();
Xextern int (*Mf_noteoff)();
Xextern int (*Mf_pressure)();
Xextern int (*Mf_parameter)();
Xextern int (*Mf_pitchbend)();
Xextern int (*Mf_program)();
Xextern int (*Mf_chanpressure)();
Xextern int (*Mf_sysex)();
Xextern int (*Mf_metamisc)();
Xextern int (*Mf_seqspecific)();
Xextern int (*Mf_seqnum)();
Xextern int (*Mf_text)();
Xextern int (*Mf_eot)();
Xextern int (*Mf_timesig)();
Xextern int (*Mf_smpte)();
Xextern int (*Mf_tempo)();
Xextern int (*Mf_keysig)();
Xextern int (*Mf_arbitrary)();
Xextern int (*Mf_error)();
Xextern long Mf_currtime;
Xextern int Mf_nomerge;
X
X/* definitions for MIDI file writing code */
Xextern int (*Mf_putc)();
Xextern int (*Mf_writetrack)();
Xextern int (*Mf_writetempotrack)();
Xfloat mf_ticks2sec();
Xunsigned long mf_sec2ticks();
Xvoid mfwrite();
X
X/* MIDI status commands most significant bit is 1 */
X#define note_off         	0x80
X#define note_on          	0x90
X#define poly_aftertouch  	0xa0
X#define control_change    	0xb0
X#define program_chng     	0xc0
X#define channel_aftertouch      0xd0
X#define pitch_wheel      	0xe0
X#define system_exclusive      	0xf0
X#define delay_packet	 	(1111)
X
X/* 7 bit controllers */
X#define damper_pedal            0x40
X#define portamento	        0x41 	
X#define sostenuto	        0x42
X#define soft_pedal	        0x43
X#define general_4               0x44
X#define	hold_2		        0x45
X#define	general_5	        0x50
X#define	general_6	        0x51
X#define general_7	        0x52
X#define general_8	        0x53
X#define tremolo_depth	        0x5c
X#define chorus_depth	        0x5d
X#define	detune		        0x5e
X#define phaser_depth	        0x5f
X
X/* parameter values */
X#define data_inc	        0x60
X#define data_dec	        0x61
X
X/* parameter selection */
X#define non_reg_lsb	        0x62
X#define non_reg_msb	        0x63
X#define reg_lsb		        0x64
X#define reg_msb		        0x65
X
X/* Standard MIDI Files meta event definitions */
X#define	meta_event		0xFF
X#define	sequence_number 	0x00
X#define	text_event		0x01
X#define copyright_notice 	0x02
X#define sequence_name    	0x03
X#define instrument_name 	0x04
X#define lyric	        	0x05
X#define marker			0x06
X#define	cue_point		0x07
X#define channel_prefix		0x20
X#define	end_of_track		0x2f
X#define	set_tempo		0x51
X#define	smpte_offset		0x54
X#define	time_signature		0x58
X#define	key_signature		0x59
X#define	sequencer_specific	0x74
X
X/* Manufacturer's ID number */
X#define Seq_Circuits (0x01) /* Sequential Circuits Inc. */
X#define Big_Briar    (0x02) /* Big Briar Inc.           */
X#define Octave       (0x03) /* Octave/Plateau           */
X#define Moog         (0x04) /* Moog Music               */
X#define Passport     (0x05) /* Passport Designs         */
X#define Lexicon      (0x06) /* Lexicon 			*/
X#define Tempi        (0x20) /* Bon Tempi                */
X#define Siel         (0x21) /* S.I.E.L.                 */
X#define Kawai        (0x41) 
X#define Roland       (0x42)
X#define Korg         (0x42)
X#define Yamaha       (0x43)
X
X/* miscellaneous definitions */
X#define MThd 0x4d546864
X#define MTrk 0x4d54726b
X#define lowerbyte(x) ((unsigned char)(x & 0xff))
X#define upperbyte(x) ((unsigned char)((x & 0xff00)>>8))
END_OF_FILE
  if test 3285 -ne `wc -c <'midifile/midifile.h'`; then
    echo shar: \"'midifile/midifile.h'\" unpacked with wrong size!
  fi
  # end of 'midifile/midifile.h'
fi
if test -f 'midifile/noteon.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'midifile/noteon.c'\"
else
  echo shar: Extracting \"'midifile/noteon.c'\" \(510 characters\)
  sed "s/^X//" >'midifile/noteon.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <ctype.h>
X#include "midifile.h"
X#include "console.h"
X
XFILE *F;
Xextern long Mf_currtime;
X
Xmygetc() { return(getc(F)); }
X
Xmynoteon(chan,note,velocity)
Xint chan;
Xunsigned char note;
Xunsigned char velocity;
X{
X	printf("%ld: chan=%d, note=%d, velocity=%d\n",Mf_currtime, chan, note, velocity);
X}
X
Xmain(argc,argv)
Xchar **argv;
X{
X	argc = ccommand(&argv);
X	
X	if ( argc > 1 )
X		F = fopen(argv[1],"r");
X	else
X		F = stdin;
X
X	Mf_getc = mygetc;
X	Mf_noteon = mynoteon;
X
X	midifile();
X
X	exit(0);
X}
X
END_OF_FILE
  if test 510 -ne `wc -c <'midifile/noteon.c'`; then
    echo shar: \"'midifile/noteon.c'\" unpacked with wrong size!
  fi
  # end of 'midifile/noteon.c'
fi
exit 0
