Blame test/midifile.3

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