Blame test/midifile.3

Packit Service db8eaa
.TH MIDIFILE 3
Packit Service db8eaa
.SH NAME
Packit Service db8eaa
mfread,mfwrite \- read and write a standard MIDI file
Packit Service db8eaa
.SH SYNOPSIS
Packit Service db8eaa
\fC#include "mfread.h"
Packit Service db8eaa
Packit Service db8eaa
mfread ()
Packit Service db8eaa
Packit Service db8eaa
.nf
Packit Service db8eaa
int (*Mf_getc) ();
Packit Service db8eaa
int (*Mf_putc) ();
Packit Service db8eaa
int (*Mf_error) (char *msg);
Packit Service db8eaa
int (*Mf_header) (int format, int ntrks, int division);
Packit Service db8eaa
int (*Mf_trackstart) ();
Packit Service db8eaa
int (*Mf_trackend) ();
Packit Service db8eaa
int (*Mf_noteon) (int chan, int pitch, int vol);
Packit Service db8eaa
int (*Mf_noteoff) (int chan, int pitch, int vol);
Packit Service db8eaa
int (*Mf_pressure) (int chan, int pitch, int pressure);
Packit Service db8eaa
int (*Mf_parameter) (int chan, int control, int value);
Packit Service db8eaa
int (*Mf_pitchbend) (int chan, int msb, int lsb);
Packit Service db8eaa
int (*Mf_program) (int chan, int program);
Packit Service db8eaa
int (*Mf_chanpressure) (int chan, int pressure);
Packit Service db8eaa
int (*Mf_sysex) (int leng, char *msg);
Packit Service db8eaa
int (*Mf_metamisc) (int type, int leng, int msg);
Packit Service db8eaa
int (*Mf_seqspecific) (int type, int leng, int msg);
Packit Service db8eaa
int (*Mf_seqnum) (int num);
Packit Service db8eaa
int (*Mf_text) (int type, int leng, int msg);
Packit Service db8eaa
int (*Mf_eot) ();
Packit Service db8eaa
int (*Mf_timesig) (int numer, int denom, int clocks, int qnotes);
Packit Service db8eaa
int (*Mf_smpte) (int hour, int min, int sec, int frame, int fract);
Packit Service db8eaa
int (*Mf_tempo) (int microsecs);
Packit Service db8eaa
int (*Mf_keysig) (int sharpflat, int minor);
Packit Service db8eaa
int (*Mf_arbitrary) (int leng, int msg);
Packit Service db8eaa
int Mf_nomerge;
Packit Service db8eaa
long Mf_currtime;
Packit Service db8eaa
.fi
Packit Service db8eaa
.sp 1
Packit Service db8eaa
mfwrite(int format, int ntracks, int division, FILE *fp)
Packit Service db8eaa
.sp 1
Packit Service db8eaa
.nf
Packit Service db8eaa
int (*Mf_writetrack)(int track);
Packit Service db8eaa
int (*Mf_writetempotrack)();
Packit Service db8eaa
Packit Service db8eaa
void mf_write_midi_event(delta, type, chan, data, size)
Packit Service db8eaa
unsigned long delta;
Packit Service db8eaa
unsigned int type,chan,size;
Packit Service db8eaa
char *data;
Packit Service db8eaa
Packit Service db8eaa
void mf_write_meta_event(delta, type, data, size)
Packit Service db8eaa
unsigned long delta;
Packit Service db8eaa
unsigned int type,chan,size;
Packit Service db8eaa
char *data;
Packit Service db8eaa
Packit Service db8eaa
void mf_write_tempo(tempo)
Packit Service db8eaa
unsigned long tempo;
Packit Service db8eaa
Packit Service db8eaa
unsigned long mf_sec2ticks(float seconds, int division, int tempo)
Packit Service db8eaa
float seconds;
Packit Service db8eaa
int division;
Packit Service db8eaa
unsigned int tempo;
Packit Service db8eaa
Packit Service db8eaa
float mf_ticks2sec(ticks, division, tempo)
Packit Service db8eaa
unsigned long ticks;
Packit Service db8eaa
int division;
Packit Service db8eaa
unsigned int tempo;
Packit Service db8eaa
.fi
Packit Service db8eaa
Packit Service db8eaa
.SH DESCRIPTION
Packit Service db8eaa
The \fCmfread\fR function reads and interprets a standard MIDI file.
Packit Service db8eaa
To use it you need to understand the general form of a
Packit Service db8eaa
MIDI file and the type of information it contains, but you don't
Packit Service db8eaa
need to know much, if anything, about the detailed format of the file
Packit Service db8eaa
and the mechanics of reading it reliably and portably.
Packit Service db8eaa
Packit Service db8eaa
The \fCmfwrite\fR function writes a standard MIDI file making
Packit Service db8eaa
use of user-defined functions that access the program's
Packit Service db8eaa
data structure.  To use it you need to define your own Mf_writetrack
Packit Service db8eaa
routine and then make use of the write_* family of routines to
Packit Service db8eaa
write out the MIDI data.  The \fCmfwrite\fR routine takes
Packit Service db8eaa
care of the file format and writing the file and track chunk headers. 
Packit Service db8eaa
Packit Service db8eaa
.SH READING STANDARD MIDI FILES
Packit Service db8eaa
A single call to \fCmfread\fR will read an entire MIDI file.
Packit Service db8eaa
The interface to \fCmfread\fR is a set of external variables
Packit Service db8eaa
named \fCMf_*\fR, most of which are function pointers to be called
Packit Service db8eaa
from within \fCmfread\fR during the process of parsing the MIDI file.
Packit Service db8eaa
Before calling \fCmfread\fR, the only
Packit Service db8eaa
requirement is that you assign a value
Packit Service db8eaa
to \fCMf_getc\fR - a pointer to a function that will return
Packit Service db8eaa
characters from the MIDI file, using \-1 to indicate EOF.
Packit Service db8eaa
All the rest of the function
Packit Service db8eaa
pointers are initialized to NULL, and the default action for each
Packit Service db8eaa
is to do nothing.  The following is a complete program using \fCmfread\fR
Packit Service db8eaa
that could serve as a 'syntax checker' for MIDI files:
Packit Service db8eaa
Packit Service db8eaa
.in +1i
Packit Service db8eaa
.ft C
Packit Service db8eaa
.nf
Packit Service db8eaa
#include <stdio.h>
Packit Service db8eaa
#include "midifile.h"
Packit Service db8eaa
Packit Service db8eaa
mygetc()
Packit Service db8eaa
{
Packit Service db8eaa
	/* use standard input */
Packit Service db8eaa
	return(getchar());
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
main()
Packit Service db8eaa
{
Packit Service db8eaa
	Mf_getc = mygetc;
Packit Service db8eaa
	mfread();
Packit Service db8eaa
	exit(0);
Packit Service db8eaa
}
Packit Service db8eaa
.fi
Packit Service db8eaa
.ft R
Packit Service db8eaa
.in -1i
Packit Service db8eaa
Packit Service db8eaa
This takes advantage of the default action when an error is detected, which
Packit Service db8eaa
is to exit silently with a return code of 1.  An error function of your
Packit Service db8eaa
own can be used by giving a value to \fCMf_error\fR; the function will be
Packit Service db8eaa
called with the error message as an argument.
Packit Service db8eaa
The other \fCMf_* variables can similarly be used to call arbitrary
Packit Service db8eaa
functions while parsing the MIDI file.  The descriptions below
Packit Service db8eaa
of the information passed to these functions is sparse; refer to
Packit Service db8eaa
the MIDI file standard for the complete descriptions.
Packit Service db8eaa
Packit Service db8eaa
\fCMf_header\fR is the first function to be called, and its arguments
Packit Service db8eaa
contain information from the MIDI file's header; the format (0,1, or 2),
Packit Service db8eaa
the number of tracks, and the division of a quarter-note that defines
Packit Service db8eaa
the times units.
Packit Service db8eaa
\fCMf_trackstart\fR and
Packit Service db8eaa
\fCMf_trackend\fR are called at the beginning and end of each track.
Packit Service db8eaa
Packit Service db8eaa
Once inside a track, each separate message causes a function to be called.
Packit Service db8eaa
For example, each note-on message causes \fCMf_noteon\fR to be called
Packit Service db8eaa
with the channel, pitch, and volume as arguments.  The time at which
Packit Service db8eaa
the message occurred is stored in \fCMf_currtime\fR - one of the few
Packit Service db8eaa
external variables that isn't a function pointer.  The other channel messages
Packit Service db8eaa
are handled in a similar and obvious fashion -
Packit Service db8eaa
\fCMf_noteoff\fR,
Packit Service db8eaa
\fCMf_pressure\fR,
Packit Service db8eaa
\fCMf_parameter\fR,
Packit Service db8eaa
\fCMf_pitchbend\fR,
Packit Service db8eaa
\fCMf_program\fR,
Packit Service db8eaa
and \fCMf_chanpressure\fR.  See the declarations above for the arguments
Packit Service db8eaa
that are passed to each.
Packit Service db8eaa
Packit Service db8eaa
System exclusive messages are handled by calling \fCMf_sysex\fR, passing
Packit Service db8eaa
as arguments the message length and a pointer to a static buffer containing
Packit Service db8eaa
the entire message.
Packit Service db8eaa
The buffer is expanded when necessary; memory availability is the only limit
Packit Service db8eaa
to its size.  Normally, 'continued' system exclusives are automatically
Packit Service db8eaa
merged, and \fCMf_sysex\fR is only called once.  It you want to disable this
Packit Service db8eaa
you can set \fCMf_nomerge\fR to 1, causing \fCMf_sysex\fR to be called
Packit Service db8eaa
once for each part of the message.
Packit Service db8eaa
Packit Service db8eaa
\fCMf_seqnum\fR is called by the \fImeta\fR message that provides
Packit Service db8eaa
a sequence number,
Packit Service db8eaa
which if present must appear at the beginning of a track.
Packit Service db8eaa
The tempo \fImeta\fR message causes \fCMf_tempo\fR to be called; its
Packit Service db8eaa
argument is the number of microseconds per MIDI quarter-note (24 MIDI clocks).
Packit Service db8eaa
The end-of-track \fImeta\fR message causes \fCMf_eot\fR to be called.
Packit Service db8eaa
The key signature \fImeta\fR message causes \fCMf_keysig\fR to be called;
Packit Service db8eaa
the first argument conveys the number of sharps or flats, the second
Packit Service db8eaa
argument is 1 if the key is minor.
Packit Service db8eaa
Packit Service db8eaa
The \fCMf_timesig\fR and \fCMf_smpte\fR functions are called when the
Packit Service db8eaa
corresponding \fImeta\fR messages are seen.  See the MIDI file standard
Packit Service db8eaa
for a description of their arguments.
Packit Service db8eaa
Packit Service db8eaa
The \fItext\fR messages in the MIDI file standard are of the following
Packit Service db8eaa
types:
Packit Service db8eaa
Packit Service db8eaa
.in +1i
Packit Service db8eaa
.nf
Packit Service db8eaa
0x01		Text Event
Packit Service db8eaa
0x02		Copyright
Packit Service db8eaa
0x03		Sequence/Track Name
Packit Service db8eaa
0x04		Instrument
Packit Service db8eaa
0x05		Lyric
Packit Service db8eaa
0x06		Marker
Packit Service db8eaa
0x07		Cue Point
Packit Service db8eaa
0x08-0x0F	Reserved but Undefined
Packit Service db8eaa
.fi
Packit Service db8eaa
.in -1i
Packit Service db8eaa
Packit Service db8eaa
\fCMf_text\fR is called for each of these; the arguments are
Packit Service db8eaa
the type number, the message length, and a pointer to the message buffer.
Packit Service db8eaa
Packit Service db8eaa
Miscellaneous \fImeta\fR messages are handled by \fCMf_metamisc\fR,
Packit Service db8eaa
sequencer-specific messages are handled by \fCMf_seqspecific\fR, and
Packit Service db8eaa
arbitrary "escape" messages (started with 0xF7) are handled by
Packit Service db8eaa
\fCMf_arbitrary\fR.
Packit Service db8eaa
.SH READING EXAMPLE
Packit Service db8eaa
The following is a \fCstrings\fR-like program for MIDI files:
Packit Service db8eaa
Packit Service db8eaa
.in +1i
Packit Service db8eaa
.ft C
Packit Service db8eaa
.nf
Packit Service db8eaa
#include <stdio.h>
Packit Service db8eaa
#include <ctype.h>
Packit Service db8eaa
#include "midifile.h"
Packit Service db8eaa
Packit Service db8eaa
FILE *F;
Packit Service db8eaa
Packit Service db8eaa
mygetc() { return(getc(F)); }
Packit Service db8eaa
Packit Service db8eaa
mytext(type,leng,msg)
Packit Service db8eaa
char *msg;
Packit Service db8eaa
{
Packit Service db8eaa
	char *p;
Packit Service db8eaa
	char *ep = msg + leng;
Packit Service db8eaa
Packit Service db8eaa
	for ( p=msg; p
Packit Service db8eaa
		putchar( isprint(*p) ? *p : '?' );
Packit Service db8eaa
	putchar('\n');
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
main(argc,argv)
Packit Service db8eaa
char **argv;
Packit Service db8eaa
{
Packit Service db8eaa
	if ( argc > 1 )
Packit Service db8eaa
		F = fopen(argv[1],"r");
Packit Service db8eaa
	else
Packit Service db8eaa
		F = stdin;
Packit Service db8eaa
Packit Service db8eaa
	Mf_getc = mygetc;
Packit Service db8eaa
	Mf_text = mytext;
Packit Service db8eaa
Packit Service db8eaa
	mfread();
Packit Service db8eaa
Packit Service db8eaa
	exit(0);
Packit Service db8eaa
}
Packit Service db8eaa
.fi
Packit Service db8eaa
.ft R
Packit Service db8eaa
.in -1i
Packit Service db8eaa
.sp
Packit Service db8eaa
.SH WRITING STANDARD MIDI FILES
Packit Service db8eaa
A single call to \fCmfwrite\fR will write an entire MIDI file.  Before
Packit Service db8eaa
calling \fCmfwrite\fR, you must assign values to function pointers
Packit Service db8eaa
\fCMf_writetrack\fR and \fCMf_putc\fR.  The first is a routine to
Packit Service db8eaa
access your MIDI data structure, which can make use of other library
Packit Service db8eaa
routines to write the actual MIDI data.  The routine
Packit Service db8eaa
\fCMf_writetrack\fR will be passed a single parameter which is the
Packit Service db8eaa
number of the track to be written.  The pointer \fCMf_putc\fR should be
Packit Service db8eaa
set to point to a routine that accepts a character as input, writes that
Packit Service db8eaa
character to a file, and returns the value that was written.  In the
Packit Service db8eaa
case of a format 1 file, a routine has to be written to write a tempo
Packit Service db8eaa
map, and assigned to the function pointer \fCMf_writetempotrack\fR.
Packit Service db8eaa
This is because format 1 files assume the first track written is a
Packit Service db8eaa
tempo track.
Packit Service db8eaa
Packit Service db8eaa
\fCmf_write_midi_event\fR and \fCmf_write_meta_event\fR are routines
Packit Service db8eaa
that should be called from your \fCMf_writetrack\fR routine to write
Packit Service db8eaa
out MIDI events.  The delta time param is the number of ticks since the
Packit Service db8eaa
last event.  The int "type" is the type of MIDI message. The int "chan"
Packit Service db8eaa
is the MIDI channel, which can be between 1 and 16.  The char pointer
Packit Service db8eaa
"data" points to an array containing the data bytes, if any exist. The
Packit Service db8eaa
int "size" is the number of data bytes.
Packit Service db8eaa
Packit Service db8eaa
\fCmf_sec2ticks\fR and \fCmf_ticks2sec\fR are utility routines
Packit Service db8eaa
to help you convert between the MIDI file parameter of ticks
Packit Service db8eaa
and the more standard seconds. The int "division" is the same
Packit Service db8eaa
division parameter from the file header, and tempo is expressed
Packit Service db8eaa
in microseconds per MIDI quarter-note, or "24ths of a microsecond
Packit Service db8eaa
per MIDI clock". The division has two meanings, depending on
Packit Service db8eaa
whether bit 15 is set or not.  If bit 15 of division is zero,
Packit Service db8eaa
bits 14 through 0 represent the number of delta-time "ticks"
Packit Service db8eaa
which make up a quarter note.  If bit 15 of division is a one,
Packit Service db8eaa
delta-times in a file correspond to subdivisions of a second
Packit Service db8eaa
similar to SMPTE and MIDI time code. In this format bits
Packit Service db8eaa
14 through 8 contain one of four values \-24, \-25, \-29, or \-30,
Packit Service db8eaa
corresponding to the four standard SMPTE and MIDI time code
Packit Service db8eaa
frame per second formats, where \-29 represents 30 drop frame.
Packit Service db8eaa
The second byte consisting of bits 7 through 0 corresponds
Packit Service db8eaa
the the resolution within a frame.  Refer the Standard MIDI Files 
Packit Service db8eaa
1.0 spec for more details.
Packit Service db8eaa
Packit Service db8eaa
.SH WRITING EXAMPLE
Packit Service db8eaa
The following is a simple program to demonstrate writing MIDI files.
Packit Service db8eaa
The track would consist of a series of quarter notes from lowest to
Packit Service db8eaa
highest in pitch at constant velocity, each separated by a quarter-note
Packit Service db8eaa
rest.
Packit Service db8eaa
.sp
Packit Service db8eaa
.in +1i
Packit Service db8eaa
.ft C
Packit Service db8eaa
.nf
Packit Service db8eaa
#include <stdio.h>
Packit Service db8eaa
#include <ctype.h>
Packit Service db8eaa
#include "midifile.h"
Packit Service db8eaa
Packit Service db8eaa
FILE *fp;
Packit Service db8eaa
myputc(c) { return(putc(c,fp));}
Packit Service db8eaa
Packit Service db8eaa
int mywritetrack(track)
Packit Service db8eaa
int track;
Packit Service db8eaa
{
Packit Service db8eaa
    int i;
Packit Service db8eaa
    char data[2];
Packit Service db8eaa
Packit Service db8eaa
    /* 120 beats/per/second */
Packit Service db8eaa
    mf_write_tempo((long)500000); 
Packit Service db8eaa
Packit Service db8eaa
    for(i = 1 ; i < 128; i++){
Packit Service db8eaa
       data[0] = i; /* note number */
Packit Service db8eaa
       data[1] = 64; /* velocity */
Packit Service db8eaa
       if(!mf_write_midi_event(480,note_on,1,data,2)) 
Packit Service db8eaa
	   return(\-1);
Packit Service db8eaa
       if(!mf_write_midi_event(480,note_off,1,data,2)) 
Packit Service db8eaa
           return(\-1);
Packit Service db8eaa
    }
Packit Service db8eaa
Packit Service db8eaa
    return(1);
Packit Service db8eaa
} /* end of write_track() */
Packit Service db8eaa
Packit Service db8eaa
main(argc,argv)
Packit Service db8eaa
char **argv;
Packit Service db8eaa
{
Packit Service db8eaa
    if((fp = fopen(argv[1],"w")) == 0L)
Packit Service db8eaa
	exit(1);
Packit Service db8eaa
Packit Service db8eaa
    Mf_putc = myputc;
Packit Service db8eaa
    Mf_writetrack = mywritetrack;
Packit Service db8eaa
Packit Service db8eaa
    /* write a single track */
Packit Service db8eaa
    mfwrite(0,1,480,fp);
Packit Service db8eaa
}
Packit Service db8eaa
.sp
Packit Service db8eaa
.fi
Packit Service db8eaa
.ft R
Packit Service db8eaa
.in -1i
Packit Service db8eaa
.sp
Packit Service db8eaa
.SH AUTHOR
Packit Service db8eaa
Tim Thompson (att!twitch!glimmer!tjt)
Packit Service db8eaa
.SH CONTRIBUTORS
Packit Service db8eaa
Michael Czeiszperger (mike@pan.com)